HDU 4836 —— The Query on the Tree(线段树+LCA)

来源:互联网 发布:php redis watch 编辑:程序博客网 时间:2024/06/05 06:49

题目:http://acm.hdu.edu.cn/showproblem.php?pid=4836

下午百度之星复赛里最简单的一题,虽然我还是1个小时才AC的,呃,下午果断被虐粗翔。

本题磨了1个小时才过,第1题还是很猥琐地用了随机数过的(真不知道怎么做)。

回到这题来,其实也不知道大牛们怎么做的,我只能用线段树+LCA搞了。

首先考虑根不改变的情况,那么我们可以将每个结点映射到线段树上去,让每个点对应的子树的点都落在某个连续区间上;

具体是在dfs的时候,用id来表示当前已经映射到线段树的编号,初始为0,进入某个结点X,令left[X]=right[X]=++id,然后继续dfs,对于X每个子节点j,dfs完了顺便更新right[X]=max(right[X], right[j]),这样在所有子节点遍历完的情况下,left[X]到right[X]就刚好是以X为根的子树的区间。在纸上模拟下就明白了。

然后对于查询和点更新就是个经典的线段树问题了。

比较麻烦的是根被改变的情况,假设我们现在要查询以X为根的子树的和,分析下当前根是root时与原先的不同。如果root原来就是X的祖先了,那么答案跟原来是一样的,再往下想其实就是只要root需要经过X原先某个祖先到达X的话,答案应该都一样的,所以这个好办。

而如果X刚好就是root,那么答案就是整棵树的和,也好处理。

剩下的就是原来X是root的祖先,我们假设X经过它的某个子节点Y到达root,那么很明显当前X的子树和,应该是整棵树的值减去原来Y对应的子树的和。因为X-Y这条边是root进行dfs到达X的最后一条边,换句话说这条边就把是不是X的子树的点分隔开了。

分析到这里问题就好办了,对于查询X,求出它们的最近公共祖先V,再进行判断即可。

#include<cstdio>#include<cstring>#include<vector>#include<algorithm>using namespace std;#define lson o<<1#define rson (o<<1)|1#define N 10001#define pb push_backvector<int> V[N];int t, ct, n;int left[N], right[N], val[N], v2[N];int s[N<<2], l[N<<2], r[N<<2];int id, root;bool f[N];int depth[N], parent[16][N];void dfs(int x){    left[x] = right[x] = ++id;    v2[id] = val[x];    for(int i=0; i<V[x].size(); i++){        int j=V[x][i];        if(f[j])    continue;        f[j]=1;        depth[j] = depth[x]+1;        parent[0][j] = x;        dfs(j);        right[x] = max(right[x], right[j]);    }}void maintain(int o){    s[o] = s[lson]+s[rson];}void build(int o, int ll, int rr){    l[o]=ll; r[o]=rr;    s[o]=0;    if(ll<rr){        int m = (ll+rr)>>1;        build(lson, ll, m);        build(rson, m+1, rr);        maintain(o);    }    else{        s[o] = v2[ll];    }}void update(int o, int p, int v){    if(l[o]==p && r[o]==p){        s[o]=v;        return;    }    int m = (l[o]+r[o])>>1;    if(p<=m)    update(lson, p, v);    else    update(rson, p, v);    maintain(o);}void init_lca(){    for(int k=0; k<15; k++){        for(int v=1; v<=n; v++){            if(parent[k][v]<0)  parent[k+1][v]=-1;            else    parent[k+1][v] = parent[k][parent[k][v]];        }    }}int lca(int u, int v){    if(depth[u]>depth[v]){        swap(u,v);    }    for(int k=0; k<16; k++){        if((depth[v]-depth[u])>>k & 1){            v = parent[k][v];        }    }    if(u==v)    return u;    for(int k=15; k>=0; k--){        if(parent[k][u]!=parent[k][v]){            u=parent[k][u];            v=parent[k][v];        }    }    return parent[0][u];}int query(int o, int ll, int rr){    if(l[o]==ll && r[o]==rr)    return s[o];    int m = (l[o]+r[o])>>1;    if(rr<=m)   return query(lson, ll, rr);    else if(ll>m)   return query(rson, ll, rr);    else    return query(lson, ll, m)+query(rson, m+1, rr);}int query(int x){    if(x==root) return s[1];    int v = lca(root, x);    if(v!=x){        return query(1, left[x], right[x]);    }    for(int i=0; i<V[x].size(); i++){        int j=V[x][i];        if(depth[j]<depth[x])   continue;        v = lca(root, j);        if(v==j){//找到前面所说的Y            return s[1] - query(1, left[j], right[j]);        }    }    return 0;}inline void in(int &x){    char c=getchar();    x=0;    while(c<48 || c>57) c=getchar();    while(c>=48 && c<=57){        x = x*10+c-48;        c = getchar();    }}int main(){    in(t);    for(ct=1; ct<=t; ct++){        printf("Case #%d:\n", ct);        in(n);        for(int i=1; i<=n; i++) V[i].clear();        int x, y;        for(int i=1; i<n; i++){            in(x); in(y);            V[x].pb(y);            V[y].pb(x);        }        for(int i=1; i<=n; i++){            in(val[i]);        }        memset(f,0,sizeof(f));        memset(parent,-1,sizeof(parent));        f[1]=1;        id = 0;        depth[1]=1;        dfs(1);        init_lca();        build(1, 1, n);        int q;        char op[10];        in(q);        root=1;        while(q--){            scanf("%s", op);            in(x);            if(op[0]=='Q'){                printf("%d\n", query(x));            }            else if(op[0]=='C'){                in(y);                update(1, left[x], y);            }            else{                root = x;            }        }    }    return 0;}



0 0
原创粉丝点击