首页 > 代码库 > 2017博普杯 东北大学邀请赛(B. Drink too much water)(贪心+树链剖分)

2017博普杯 东北大学邀请赛(B. Drink too much water)(贪心+树链剖分)

 题目地址:https://oj.neu.edu.cn/problem/1204

 

题目大意:

其实就是树上的线段覆盖,

给出一棵n个结点的树,然后给出树上的一些路径进行覆盖,然后要求选取最少的点,能够把这些线段都占有

(或者说:一开始树上每个结点权值都为0,选取最少的点,把它们的权重变成1,使得询问的每一条路径上有含有权值为1的结点)

 

题解:

类似线段覆盖(线段覆盖是按照右端点贪心)

这个题就是按照每个路径的lca的深度贪心

也就是说把询问按照lca的深度从大到小排序

然后依次枚举询问

如果当前询问的路径没有权值为1的结点,就把lca赋值成1,答案加1

如果有就跳过

最后输出就可以了

整个过程用树链剖分就可以维护

(第一次wa是没有多组输入输出,第二次wa是把return 0 放在多组数据里了,第三次wa是忘记删freopen。。。orz)

 

#include <algorithm>
#include <cstring>
#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;
const int maxn = 1e5 + 100;
const int maxm = 5e5 + 100;
int tree[maxn*4], deep[maxn], p[maxn], sz[maxn], son[maxn], top[maxn], F[maxn];
int tot = 0;
vector<int> G[maxn];
struct Que{
    int x, y, lca;
    bool operator <(const Que& B) const{
        return deep[lca] < deep[B.lca];
    }
};
vector<Que> Q;
void Insert(int o, int l, int r, int k, int v){
    if(l == r) { tree[o] = v; return; }
    int mid = (l+r)>>1;
    if(k <= mid) Insert(o*2, l, mid, k, v);
    else Insert(o*2+1, mid+1, r, k, v);
    tree[o] = max(tree[o*2], tree[o*2+1]);
}
int Query(int o, int l, int r, int L, int R){
    if(L <= l && r <= R) return tree[o];
    int mid = (l+r)>>1, ans = 0;
    if(L <= mid) ans = max(ans, Query(o*2, l, mid, L, R));
    if(R > mid) ans = max(ans, Query(o*2+1, mid+1, r, L, R));
    return ans;
}

int dfs1(int x, int fa, int d){
    deep[x] = d;
    p[x] = fa;
    sz[x] = 1;
    for(auto to : G[x]){
        if(fa == to) continue;
        sz[x] += dfs1(to, x, d+1);
        if(sz[to] > sz[son[x]]) son[x] = to;
    }
    return sz[x];
}
void dfs2(int x, int fa){
    F[x] = ++tot;
    if(son[fa] == x) top[x] = top[fa];
    else top[x] = x;
    if(son[x]) dfs2(son[x], x);
    for(auto to : G[x]){
        if(to == fa || to == son[x]) continue;
        dfs2(to, x);
    }
}
int TQuery(int x, int y, int &lca){
    int ans = 0;
    while(top[x] != top[y]){
        if(deep[top[y]] > deep[top[x]]) swap(x, y);
        ans = max(ans, Query(1, 1, tot, F[top[x]], F[x]));
        x = p[top[x]];
    }
    if(deep[x] > deep[y]) swap(x, y);
    ans = max(ans, Query(1, 1, tot, F[x], F[y]));
    lca = x;
    return ans;
}

int main()
{

    int n, m, x, y;
    while(cin>>n){
        tot = 0;
        for(int i = 0; i <= n; i++) G[i].clear();
        memset(son, 0, sizeof(son));
        memset(tree, 0, sizeof(tree));
        for(int i = 1; i <= n; i++){
            scanf("%d %d", &x, &y);
            G[x].push_back(y);
            G[y].push_back(x);
        }
        dfs1(0, 0, 1);
        dfs2(0, 0);
        cin>>m;
        Q.resize(m);
        for(int i = 0; i < m; i++){
            scanf("%d %d", &Q[i].x, &Q[i].y);
            TQuery(Q[i].x, Q[i].y, Q[i].lca);
        }
        sort(Q.begin(), Q.end());
        reverse(Q.begin(), Q.end());
        int ans = 0;
        for(auto a : Q){
            int k = TQuery(a.x, a.y, a.lca);
            if(k) continue;
            else Insert(1, 1, tot, F[a.lca], 1), ans++;
        }
        cout<<ans<<endl;
    }
    return 0;
}

 

2017博普杯 东北大学邀请赛(B. Drink too much water)(贪心+树链剖分)