ustc 1303 Special tree
http://acm.ustc.edu.cn/ustcoj/problem.php?id=1303
/*
题意:给一棵树,树上每个节点都有一个值。对于一棵树,如果断开某一条边,就会形成两个连通分量。现有两种操作:
Query x:如果断开第x条边,输出两个连通分量里的最大值,值小的在前面。
Add x a b:如果断开第x条边形成的两个连通分量里节点的个数不相等,则节点多的连通分量里都加上max(a,b),节点少的连通分量里都加上min(a,b).
思路:原题是这个吧:http://acm.xmu.edu.cn/JudgeOnline/problem.php?id=1333
dfs遍历一次树,就可以得到一个序列,也就得到每个节点管理的区间。
用线段树维护每个节点下的最大值和最小值就行了,区间更新。
*/

#include<stdio.h> #include<string.h> #include<iostream> using namespace std; const int maxn = 10005; struct nd{int next,v;}edge[maxn*2]; struct nnd{int s,tp,c,mx,mn;}as[maxn*4]; int head[maxn],vis[maxn],ls[maxn][2]; int val[maxn],E[maxn][2]; int n,m,ecnt,idx; inline int min(int x,int y){return x > y ? y : x;} inline int max(int x,int y){return x > y ? x : y;} void add(int u,int v) { edge[ecnt].v = v; edge[ecnt].next = head[u]; head[u] = ecnt++; } int getlist(int pre,int x) { vis[++idx] = x; ls[x][0] = ls[x][1] = idx;//x节点的管理的区间为[ ls[x][0],ls[x][1] ]。 for(int i = head[x]; ~i; i = edge[i].next){ if(edge[i].v==pre)continue; ls[x][1]=getlist(x,edge[i].v); } return ls[x][1]; } void readin() { int i; scanf("%d %d",&n,&m); for(i = 1; i <= n; ++ i) scanf("%d",val+i); ecnt = 0; memset(head,-1,sizeof(head)); for(i = 1; i < n; ++ i){ scanf("%d %d",&E[i][0],&E[i][1]); add(E[i][0],E[i][1]); add(E[i][1],E[i][0]); } idx = 0; getlist(-1,1); } void pushup(int rt) { as[rt].mn = min(as[rt*2].mn,as[rt*2+1].mn); as[rt].mx = max(as[rt*2].mx,as[rt*2+1].mx); } void pushdown(int rt) { as[rt*2].tp += as[rt].tp; as[rt*2+1].tp += as[rt].tp; as[rt*2].s += as[rt].tp; as[rt*2+1].s += as[rt].tp; as[rt*2].mn += as[rt].tp; as[rt*2+1].mn += as[rt].tp; as[rt*2].mx += as[rt].tp; as[rt*2+1].mx += as[rt].tp; as[rt].tp = 0; } void build(int rt,int l,int r) { int m = (l + r) >> 1; as[rt].tp = 0; if(l==r){ as[rt].mn = as[rt].mx = as[rt].s = val[vis[l]]; as[rt].c = 1; return ; } build(rt*2,l,m); build(rt*2+1,m+1,r); as[rt].c = as[rt*2].c + as[rt*2+1].c; pushup(rt); } void update(int rt,int l,int r,int L,int R,int val) { int m = (l + r) >> 1; if(l==L && r==R){ as[rt].tp += val; as[rt].mn += val; as[rt].mx += val; as[rt].s += val; return ; } if(as[rt].tp)pushdown(rt); if(L>m)update(rt*2+1,m+1,r,L,R,val); else if(R<=m)update(rt*2,l,m,L,R,val); else{ update(rt*2,l,m,L,m,val); update(rt*2+1,m+1,r,m+1,R,val); } pushup(rt); } int c,mn,mx; void query(int rt,int l,int r,int L,int R) { int m = (l + r) >> 1; if(l>r)return; if(l==L && r==R){ c = as[rt].c; mn = as[rt].mn; mx = as[rt].mx; return; } if(as[rt].tp)pushdown(rt); if(L>m)query(rt*2+1,m+1,r,L,R); else if(R<=m)query(rt*2,l,m,L,R); else{ query(rt*2,l,m,L,m); int C = c,Mn = mn, Mx = mx; query(rt*2+1,m+1,r,m+1,R); c += C; mn = min(Mn,mn); mx = max(Mx,mx); } pushup(rt); } int L,R; void cal(int x)//断开第x条边时,会分出三个区间:[1,L-1] [L,R] [R+1,n] { int a = E[x][0]; int b = E[x][1]; if(ls[a][0]>ls[b][0]){ L = ls[a][0]; R = ls[a][1]; }else{ L = ls[b][0]; R = ls[b][1]; } } void processing() { int i,a,b,Mx,Mn; char op[10]; build(1,1,n); while(m--){ scanf("%s",op); if(op[0]=='Q'){ scanf("%d",&i); cal(i); query(1,1,n,L,R); Mx = mx; query(1,1,n,1,L-1); Mn = mx; if(R+1<=n){ query(1,1,n,R+1,n); Mn = max(mx,Mn); } printf("%d %d\n",min(Mx,Mn),max(Mx,Mn)); }else{ scanf("%d %d %d",&i,&a,&b); cal(i); query(1,1,n,L,R); if(c*2==n)continue; else{ if(c*2>n){ update(1,1,n,L,R,max(a,b)); update(1,1,n,1,L-1,min(a,b)); if(R+1<=n)update(1,1,n,R+1,n,min(a,b)); }else{ update(1,1,n,L,R,min(a,b)); update(1,1,n,1,L-1,max(a,b)); if(R+1<=n)update(1,1,n,R+1,n,max(a,b)); } } } } } int main() { int T; for(scanf("%d",&T);T--;){ readin(); processing(); } return 0; }