题意:一棵树,每个节点有初始权值,有p次操作,要么询问某个点的权值,要么修改树上两个节点u-v间路径的权值。
思路:树链剖分。这是我做的第一道树链剖分题,我对树链剖分的理解是,把树递归地分割成线性(重边优先),按顺序排列起来,最后长度和树的节点数一样。然后就可以利用线段树,对树上的路径进行维护。
#pragma comment(linker, "/STACK:1024000000,1024000000") #include<iostream> #include<cmath> #include<queue> #include<map> #include<set> #include<vector> #include<algorithm> #include<string.h> #include<cstdio> using namespace std; #define maxn 50010 int a[maxn]; int head[maxn]; int next[maxn*2]; int to[maxn*2]; int siz[maxn]; //以i为根的子树的节点数 int dep[maxn]; //节点深度 int top[maxn]; //所在链的顶端节点 int fa[maxn]; //父节点 int son[maxn]; //重儿子 int w[maxn]; //节点在线段树中的位置 int w_1[maxn]; bool vis[maxn]; int pos; int end; int n,m,p; void init(int n){ for(int i=1;i<=n;i++){ siz[i]=top[i]=son[i]=0; dep[i]=w[i]=w_1[i]=fa[i]=0; head[i]=-1; } pos=0; end=0; } void addedge(int u,int v){ to[end]=v,next[end]=head[u],head[u]=end++; to[end]=u,next[end]=head[v],head[v]=end++; } void dfs(int x,int pre){ vis[x]=1; dep[x]=dep[pre]+1; fa[x]=pre; for(int i=head[x];i!=-1;i=next[i]){ if(to[i]==pre)continue; if(vis[to[i]])continue; dfs(to[i],x); siz[x]+=siz[to[i]]; if(son[x]==0)son[x]=to[i]; else if(siz[son[x]]<siz[to[i]])son[x]=to[i]; } siz[x]++; } void dfs2(int x,int pre){ vis[x]=1; pos++; w[x]=pos; w_1[pos]=x; if(son[fa[x]]==x)top[x]=top[fa[x]]; else top[x]=x; //先递归重儿子 if(son[x]==0)return; dfs2(son[x],x); for(int i=head[x];i!=-1;i=next[i]){ if(to[i]==pre)continue; if(to[i]==son[x])continue; if(vis[to[i]])continue; dfs2(to[i],x); } } struct node{ int l,r; int val; int lazy; }; node tree[maxn<<2]; void build_tree(int n,int l,int r){ tree[n].l=l; tree[n].r=r; tree[n].val=tree[n].lazy=0; if(l==r){ tree[n].val=a[w_1[l]]; return; } int mid=(l+r)>>1; build_tree(n<<1,l,mid); build_tree((n<<1)|1,mid+1,r); } void push_down(int x){ int& add=tree[x].lazy; node& lch=tree[x<<1]; node& rch=tree[(x<<1)|1]; if(lch.l==lch.r)lch.val+=add; else lch.lazy+=add; if(rch.l==rch.r)rch.val+=add; else rch.lazy+=add; add=0; } void update(int n,int l,int r,int v){ if(tree[n].l==l&&tree[n].r==r){ if(l==r){ tree[n].val+=v; }else{ tree[n].lazy+=v; } return; } if(tree[n].lazy)push_down(n); int mid=(tree[n].l+tree[n].r)>>1; if(r<=mid){ update(n<<1,l,r,v); }else{ if(l>mid){ update((n<<1)|1,l,r,v); }else{ update(n<<1,l,mid,v); update((n<<1)|1,mid+1,r,v); } } } int query(int n,int pos){ if(tree[n].l==tree[n].r)return tree[n].val; if(tree[n].lazy)push_down(n); int mid=(tree[n].l+tree[n].r)>>1; if(pos<=mid) return query(n<<1,pos); else return query((n<<1)|1,pos); } int main(){ while(cin>>n>>m>>p){ init(n); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); } for(int i=1;i<=m;i++){ int u,v; scanf("%d%d",&u,&v); addedge(u,v); } memset(vis,0,sizeof(vis)); dfs(1,0); memset(vis,0,sizeof(vis)); dfs2(1,0); build_tree(1,1,n); char op[2]; for(int i=1;i<=p;i++){ scanf("%s",op); if(op[0]=='Q'){ int c; scanf("%d",&c); printf("%d\n",query(1,w[c])); }else{ int u,v,k; scanf("%d%d%d",&u,&v,&k); if(op[0]=='D')k=-k; //不在一条路径上,上爬 while(top[u]!=top[v]){// if(dep[top[u]]<dep[top[v]])swap(u,v); update(1,w[top[u]],w[u],k); u=fa[top[u]]; } //在一条路径上 if(w[u]>w[v])swap(u,v); update(1,w[u],w[v],k); } } } return 0; }