hdu 4836 The Query on the Tree(线段树or树状数组)
The Query on the Tree
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 57 Accepted Submission(s): 20
Problem Description
度度熊近期沉迷在和树有关的游戏了。他一直觉得树是最奇妙的数据结构。
一天他遇到这样一个问题:
有一棵树,树的每一个点有点权,每次有三种操作:
1. Query x 表示查询以x为根的子树的权值和。
2. Change x y 表示把x点的权值改为y(0<=y<=100)。
3. Root x 表示把x变为根。
如今度度熊想请更聪明的你帮助解决问题。
Input
第一行为数据组数T(1 <= T <= 100)
每组数据第一行为N(1<= N <= 10000),表示树的节点数。
后面N-1行每行有两个数x,y 。表示x,y之间有一条边 1<=x,y<=N。初始时树是以1号节点为根节点。
每组数据第一行为N(1<= N <= 10000),表示树的节点数。
后面N-1行每行有两个数x,y 。表示x,y之间有一条边 1<=x,y<=N。初始时树是以1号节点为根节点。
之后的一行为N个数表示这N个点的点权(点权的范围是0到100)。
然后为整数Q(Q<=1000)为操作次数。
之后的Q行为描写叙述中的三种操作。
Output
对于第k组输入数据。第一行输出Case #k 接下来对于每一个”Query x”操作。输出以x为根的子数和。
Sample Input
2 5 1 2 1 3 3 4 3 5 1 2 3 4 5 5 Query 1 Change 3 10 Query 1 Root 4 Query 3 8 1 2 1 3 3 4 4 5 5 6 5 7 4 8 1 2 3 4 5 6 7 8 5 Query 1 Query 3 Root 5 Query 3 Query 1
Sample Output
Case #1: 15 22 18 Case #2: 36 33 6 3
题意:天朝文字不解释
题解:我先将树的点以1为根转换成一个数组并记录每一个点子树的范围。就是类似lca的那个树转数组一样,将一个点分成2个点,dfs的时候到达结点时候生成一个点值为结点本身的值,离开结点生成一个点值为0,然后保存其这2个点的位置,那么求某个点的子树和就变成了求该个区间和了,区间和操作和改变某个点的值都能够用线段树or树状数组维护。当须要更改根的时候仅记录root。我们的树始终以1为根,不作其他改变。那么查询操作。若root不是x的子树的结点,x的子树和就是x相应的区间和;若root是x的子树的结点。就要找出root是x哪个儿子的子树,然后x的子树和就是全部数的值减去那个包括root结点的子树的和。
#include<stdio.h> #include<string.h> #include<algorithm> #include<vector> using namespace std; struct node{ int y,next; }e[80008]; int head[80008],val[80008],tree[80008]; int sta[80008],ssta[80008],fin[80008]; int a[80008],n,all; vector<int>v[80008]; void add(int x,int y) { e[all].next=head[x]; e[all].y=y; head[x]=all++; } void dfs(int x,int &cou,int fa) { a[cou]=val[x]; sta[x]=cou++; for(int i=head[x];i!=-1;i=e[i].next) { if(fa==e[i].y) continue; dfs(e[i].y,cou,x); v[x].push_back(sta[e[i].y]); } fin[x]=cou++; } void build(int l,int r,int pos) { int mid=(l+r)>>1; if(l==r){ tree[pos]=a[l]; return; } build(l,mid,pos<<1); build(mid+1,r,pos<<1|1); tree[pos]=tree[pos<<1]+tree[pos<<1|1]; } void updata(int l,int r,int pos,int x,int y) { int mid=(l+r)>>1; if(l==r){ tree[pos]=y; return; } if(x<=mid) updata(l,mid,pos<<1,x,y); else updata(mid+1,r,pos<<1|1,x,y); tree[pos]=tree[pos<<1]+tree[pos<<1|1]; } int query(int l,int r,int pos,int templ,int tempr) { int mid=(l+r)>>1; if(templ<=l&&r<=tempr) return tree[pos]; if(tempr<=mid) return query(l,mid,pos<<1,templ,tempr); else if(templ>mid) return query(mid+1,r,pos<<1|1,templ,tempr); return query(l,mid,pos<<1,templ,mid)+query(mid+1,r,pos<<1|1,mid+1,tempr); } int main() { int t,x,y,m,cas=1; char s[18]; scanf("%d",&t); while(t--) { memset(head,-1,sizeof(head)); memset(a,0,sizeof(a)); all=0; scanf("%d",&n); for(int i=1;i<n;i++) { scanf("%d%d",&x,&y); add(x,y),add(y,x); } int cou=1; for(int i=1;i<=n;i++) { scanf("%d",val+i); v[i].clear(); } dfs(1,cou,-1); for(int i=1;i<=n;i++) { ssta[sta[i]]=i; if(v[i].size()<=0) continue; sort(v[i].begin(),v[i].end()); } build(1,cou-1,1); scanf("%d",&m); int root=1; printf("Case #%d:\n",cas++); while(m--) { scanf("%s",s); if(s[0]=='C') { scanf("%d%d",&x,&y); updata(1,cou-1,1,sta[x],y); } else if(s[0]=='R') scanf("%d",&root); else { scanf("%d",&x); if(x==root) printf("%d\n",tree[1]); else if(sta[x]<sta[root]&&fin[root]<fin[x]) { int temp=upper_bound(v[x].begin(),v[x].end(),sta[root]) - v[x].begin(); temp=ssta[v[x][temp-1]]; printf("%d\n",tree[1]-query(1,cou-1,1,sta[temp],fin[temp])); } else printf("%d\n",query(1,cou-1,1,sta[x],fin[x])); } } } return 0; }