HDU-5957 Query on a graph(线段树+树剖)

来源:互联网 发布:linux启动.sh命令 编辑:程序博客网 时间:2024/06/09 17:03

Query on a graph

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 325    Accepted Submission(s): 73


Problem Description
You are given a connected simple graph(in which both multiple edges and loops are disallowed) with N nodes and N edges. In this graph each node has a weight, and each edge has the same length of one unit. Define D(u,v) as the distance between node u and node v. Define S(u,k) as the set of nodes x which satisfy D(u,x) ≤ k.
We will ask you to perform some instructions of the following forms.
MODIFY u k d: weight of all nodes in S(u,k) increase by d or decrease by -d.
QUERY u k: ask for the sum of weight of all nodes in S(u,k).
In the beginning, the weight of all nodes are 0.
 

Input
The first line of input contains an integer t, the number of test cases. t test cases follow. For each test case, in the first line there is an integer N(N ≤ 100000). The i-th line of the next N line describes the i-th edge: two integers u,v denotes an edge between u and v. In the next line, an integer Q(Q ≤ 100000) indicates the number of instructions. Next Q lines contain instructions MODIFY u k d or QUERY u k, where |d|≤ 100 and 0 ≤ k ≤ 2.
 

Output
For each QUERY instruction, output a integer in a line.
 

Sample Input
261 22 33 44 14 53 65MODIFY 1 1 3MODIFY 3 1 2MODIFY 5 2 1QUERY 3 2QUERY 4 161 22 33 11 42 53 65MODIFY 3 1 5MODIFY 2 2 2QUERY 6 1MODIFY 4 1 -2QUERY 2 2
 

Sample Output
21141428


题意:一个图有n个点,n条边(一定存在环,但没有自环和重边)
定义D(u,v)为u到v的距离,S(u,k)为所有D(u,v)<=k的节点v的集合
有m次询问(0<=k<=2):
①u k d:将集合S(u,k)的所有节点的权值加d
②u k:询问集合S(u,k)的所有节点的权值之和


题解:线段树
首先将图分为2个部分:1个环和若干棵树(每棵树的根节点在环上)
这一步可以通过拓扑排序完成

设val[u]为节点u的权值,fa[u]为父亲,son[u]为儿子,sson[u]孙子
根据k的取值分情况讨论:
①k = 0时
    ans = val[u]
②k = 1时
    1)u为树上节点:ans = ∑val[son[u]] + val[u] + val[fa[u]]
    2)u为环上节点:ans = ∑val[son[u]] + val[u] + val[u在环上的邻居]
③k = 2时
    设father = fa[u]
    1)u为树上节点:ans = ∑val[son[u]] + ∑val[sson[u]] + val[father] + ∑val[son[father]]
        (1)father为树上节点:ans += val[fa[father]]
        (2)father为环上节点:ans += ∑val[father在环上的邻居]
    2)u为环上节点:ans = ∑val[son[u]] + ∑val[sson[u]] + val[u在环上的邻居] + val[邻居的邻居] + ∑val[son[邻居]]

知道ans该怎么求解了,重点是怎么求和。逐个求和的话复杂度高达O(n^2)显然是不行的,因此需要
用线段树进行区间维护。我们可以知道,对一个棵树求BFS序后,深度相同的节点的序号是相邻的。
对于节点u,如果知道它儿子的最小BFS序号L和最大BFS序号R,那么它儿子的所有序号就在[L,R]中。

因此我们就有了维护权值的方法:类似于树链剖分,我们将所有节点都用BFS序编号并存入线段树,
如果要访问某个节点u的所有儿子,只要知道其序号区间[L,R],这个区间序号可以用sonL[u]和sonR[u]
维护,类似的u孙子的区间序号也可以用ssonL[u]和ssonR[u]存放

至此问题就解决了,由于用到线段树,总复杂度为O(mlogn)

#include<bits/stdc++.h>#define lson l,m,rt<<1#define rson m+1,r,rt<<1|1using namespace std;typedef long long LL;const int MX = 1e5 + 5;const int INF = 0x3f3f3f3f;struct Edge{    int v,nxt;}E[MX*2];int head[MX],IN[MX],tot,n;void init_edge(){    for(int i=1;i<=n;i++) {IN[i]=0;head[i]=-1;}    tot=0;}void add_edge(int u,int v){    E[tot].v=v;    E[tot].nxt=head[u];    head[u]=tot++;    IN[v]++;}void top_sort(){    queue<int>q;    for(int i=1;i<=n;i++) if(IN[i]==1) q.push(i);    while(!q.empty()){        int u=q.front();q.pop();        for(int i=head[u];~i;i=E[i].nxt){            int v=E[i].v;            if(IN[v]>1){                IN[v]--;                if(IN[v]==1) q.push(v);            }        }    }}int p[MX],fp[MX],fa[MX],sz;int sonL[MX],sonR[MX],ssonL[MX],ssonR[MX];void bfs(int top){    queue<int>q;    q.push(top);    while(!q.empty()){        int u=q.front();q.pop();        sonL[u]=ssonL[u]=INF;        sonR[u]=ssonR[u]=0;        //printf("[%d %d]\n",u,p[u]);        for(int i=head[u];~i;i=E[i].nxt){            int v=E[i].v;            if(IN[v]>1||v==fa[u]) continue;            p[v]=++sz;            fp[sz]=v;            fa[v]=u;            sonL[u]=min(sonL[u],p[v]);            sonR[u]=max(sonR[u],p[v]);            q.push(v);        }        ssonL[fa[u]]=min(ssonL[fa[u]],sonL[u]);        ssonR[fa[u]]=max(ssonR[fa[u]],sonR[u]);    }}LL sum[MX<<2],add[MX<<2];void build(int l,int r,int rt){    sum[rt]=add[rt]=0;    if(l==r) return;    int m=(l+r)>>1;    build(lson);    build(rson);}void PushDown(int m,int rt){    if(add[rt]){        add[rt<<1]+=add[rt];        add[rt<<1|1]+=add[rt];        sum[rt<<1]+=add[rt]*(m-(m>>1));        sum[rt<<1|1]+=add[rt]*(m>>1);        add[rt]=0;    }}void PushUP(int rt){    sum[rt]=sum[rt<<1]+sum[rt<<1|1];}void update(int L,int R,int c,int l,int r,int rt){    if(L==0||R==0) return;    if(L<=l&&R>=r){        sum[rt]+=(LL)(r-l+1)*c;        add[rt]+=c;        return;    }    PushDown(r-l+1,rt);    int m=(l+r)>>1;    if(L<=m) update(L,R,c,lson);    if(R>m) update(L,R,c,rson);    PushUP(rt);}LL query(int L,int R,int l,int r,int rt){    if(L==0||R==0) return 0;    if(L<=l&&R>=r) return sum[rt];    PushDown(r-l+1,rt);    int m=(l+r)>>1;    LL ret=0;    if(L<=m) ret+=query(L,R,lson);    if(R>m) ret+=query(L,R,rson);    PushUP(rt);    return ret;}int ver[MX][2];void init(){    for(int i=1;i<=n;i++) fa[i]=0;    sz=0;}void pre_solve(){    init();    top_sort();    for(int u=1;u<=n;u++) if(IN[u]>1){        int j=0;        for(int i=head[u];~i;i=E[i].nxt){            int v=E[i].v;            if(IN[v]>1) ver[u][j++]=v;        }        p[u]=++sz;        fp[sz]=u;        bfs(u);    }    build(1,n,1);}void solve_modify(int u,int k,int d){    int father=fa[u];    if(k==0) update(p[u],p[u],d,1,n,1);    else if(k==1){        update(sonL[u],sonR[u],d,1,n,1);        update(p[u],p[u],d,1,n,1);        if(IN[u]==1) update(p[father],p[father],d,1,n,1);        else{            for(int i=0;i<2;i++){                int v=ver[u][i];                update(p[v],p[v],d,1,n,1);            }        }    }    else{        update(sonL[u],sonR[u],d,1,n,1);        update(ssonL[u],ssonR[u],d,1,n,1);        if(IN[u]==1){            update(p[father],p[father],d,1,n,1);            update(sonL[father],sonR[father],d,1,n,1);            if(IN[father]==1) update(p[fa[father]],p[fa[father]],d,1,n,1);            else{                for(int i=0;i<2;i++){                    int v=ver[father][i];                    update(p[v],p[v],d,1,n,1);                }            }        }        else{            update(p[u],p[u],d,1,n,1);            int vv[2],cnt=0;            for(int i=0;i<2;i++){                int v=ver[u][i];                update(p[v],p[v],d,1,n,1);                update(sonL[v],sonR[v],d,1,n,1);                for(int j=0;j<2;j++){                    if(ver[v][j]==u||ver[v][j]==ver[u][0]||ver[v][j]==ver[u][1]) continue;                    if(cnt>0&&ver[v][j]==vv[cnt-1]) continue;                    vv[cnt++]=ver[v][j];                }            }            for(int i=0;i<cnt;i++){                update(p[vv[i]],p[vv[i]],d,1,n,1);            }        }    }}LL solve_query(int u,int k){    int father=fa[u];    LL ret=0;    if(k==0) ret+=query(p[u],p[u],1,n,1);    else if(k==1){        ret+=query(sonL[u],sonR[u],1,n,1);        ret+=query(p[u],p[u],1,n,1);        if(IN[u]==1) ret+=query(p[father],p[father],1,n,1);        else{            for(int i=0;i<2;i++){                int v=ver[u][i];                ret+=query(p[v],p[v],1,n,1);            }        }    }    else{        ret+=query(sonL[u],sonR[u],1,n,1);        ret+=query(ssonL[u],ssonR[u],1,n,1);        if(IN[u]==1){            ret+=query(p[father],p[father],1,n,1);            ret+=query(sonL[father],sonR[father],1,n,1);            if(IN[father]==1) ret+=query(p[fa[father]],p[fa[father]],1,n,1);            else{                for(int i=0;i<2;i++){                    int v=ver[father][i];                    ret+=query(p[v],p[v],1,n,1);                }            }        }        else{            ret+=query(p[u],p[u],1,n,1);            int vv[2],cnt=0;            for(int i=0;i<2;i++){                int v=ver[u][i];                ret+=query(p[v],p[v],1,n,1);                ret+=query(sonL[v],sonR[v],1,n,1);                for(int j=0;j<2;j++){                    if(ver[v][j]==u||ver[v][j]==ver[u][0]||ver[v][j]==ver[u][1]) continue;                    if(cnt>0&&ver[v][j]==vv[cnt-1]) continue;                    vv[cnt++]=ver[v][j];                }            }            for(int i=0;i<cnt;i++){                ret+=query(p[vv[i]],p[vv[i]],1,n,1);            }        }    }    return ret;}int main(){    //freopen("in.txt","r",stdin);    int T,m,u,v,k,d;    char op[10];    scanf("%d",&T);    while(T--){        scanf("%d",&n);        init_edge();        for(int i=1;i<=n;i++){            scanf("%d%d",&u,&v);            add_edge(u,v);add_edge(v,u);        }        pre_solve();        scanf("%d",&m);        while(m--){            scanf("%s",op);            if(op[0]=='M'){                scanf("%d%d%d",&u,&k,&d);                solve_modify(u,k,d);            }            else {                scanf("%d%d",&u,&k);                printf("%lld\n",solve_query(u,k));            }        }    }    return 0;}