[ZJOI2008]树的统计
题目描述
一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。
我们将以下面的形式来要求你对这棵树完成一些操作:
I. CHANGE u t : 把结点u的权值改为t
II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值
III. QSUM u v: 询问从点u到点v的路径上的节点的权值和
注意:从点u到点v的路径上的节点包括u和v本身
输入输出格式
**输入格式:
** 输入文件的第一行为一个整数n,表示节点的个数。
接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有一条边相连。
接下来一行n个整数,第i个整数wi表示节点i的权值。
接下来1行,为一个整数q,表示操作的总数。
接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。
输出格式:
对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。
输入输出样例
输入样例#1:
4 1 2 2 3 4 1 4 2 1 3
12 QMAX 3 4
QMAX 3 3
QMAX 3 2
QMAX 2 3
QSUM 3 4
QSUM 2 1
CHANGE 1 5
QMAX 3 4
CHANGE 3 6
QMAX 3 4
QMAX 2 4
QSUM 3 4
输出样例#1:
4 1 2 2 10 6 5 6 5 16
solution
最喜欢一些难度虚高的题,比如说这一道。
这道题就是最简单的树链剖分了,基本不需要什么其它的技巧了
然而我的代码依旧在BZOJ上RE。
题目要求查询最大值和区间和,活生生的套路,只要会基本的树链剖分这道题就可以A掉
值得注意的是最大值初始赋为负数,因为权值可以为负(不然只有十分)
代码
#include<bits/stdc++.h> using namespace std; const int MAXN = 30000 + 10; const int inf = 1<<30; inline int read() { char ch; int fl=1; int xx=0; do{ ch= getchar(); if(ch=='-') fl=-1; }while(ch<'0'||ch>'9'); do{ xx=(xx<<3)+(xx<<1)+ch-'0'; ch=getchar(); }while(ch>='0'&&ch<='9'); return xx*fl; } inline int Max(int a,int b) { if(a>=b) return a; else return b; } int n,q; string op; int w[MAXN]; struct node { int to; int next; };node g[MAXN*2]; int cnt=0,head[MAXN]; inline void addedge(int a,int b) { ++cnt;g[cnt].to=b;g[cnt].next=head[a];head[a]=cnt;return; } int fa[MAXN],dep[MAXN],son[MAXN],tot[MAXN]; #define v g[i].to inline void dfs(int now) { son[now]=0,tot[now]=1; for(int i=head[now];i>0;i=g[i].next){ if(v!=fa[now]){ fa[v]=now; dep[v]=dep[now]+1; dfs(v); if(tot[son[now]]<tot[v]) son[now]=v; tot[now]+=tot[v]; } } } int num[MAXN],top[MAXN],h=0; inline void dfs2(int now,int t) { top[now]=t;++h;num[now]=h; if(son[now]!=0) dfs2(son[now],t); for(int i=head[now];i>0;i=g[i].next) { if(v!=fa[now]&&v!=son[now]) dfs2(v,v); } } struct s_tree { int l; int r; int sum; long long maxn; inline int mid() { return (l+r)>>1; } };s_tree tree[MAXN*4]; #define lc o<<1 #define rc o<<1|1 inline void build(int o,int l,int r) { tree[o].l=l; tree[o].r=r; if(l==r) { tree[o].maxn=-inf; tree[o].sum=0; } else { int mid=tree[o].mid(); build(lc,l,mid); build(rc,mid+1,r); } } inline void change(int o,int x,int y) { if(tree[o].l==tree[o].r){ tree[o].maxn=tree[o].sum=y; return; } else { int mid=tree[o].mid(); if(x<=mid) change(lc,x,y); else change(rc,x,y); tree[o].maxn=Max(tree[lc].maxn,tree[rc].maxn); tree[o].sum=tree[lc].sum+tree[rc].sum; } } inline long long findmax(int o,int x,int y) { int l=tree[o].l; int r=tree[o].r; if(x==l&&y==r) return tree[o].maxn; else { int mid=tree[o].mid(); if(x>mid) return findmax(rc,x,y); else if(y<=mid) return findmax(lc,x,y); else return Max(findmax(lc,x,mid),findmax(rc,mid+1,y)); } } inline long long findsum(int o,int x,int y) { int l=tree[o].l; int r=tree[o].r; if(x==l&&y==r) return tree[o].sum; else { int mid=tree[o].mid(); if(x>mid) return findsum(rc,x,y); else if(y<=mid) return findsum(lc,x,y); else return (findsum(lc,x,mid)+findsum(rc,mid+1,y)); } } inline long long qmax(int x,int y) { int tx=top[x],ty=top[y],ans=-inf; while(tx!=ty){ if(dep[tx]>dep[ty]) { swap(x,y); swap(tx,ty); } ans=Max(ans,findmax(1,num[ty],num[y])); y=fa[ty];ty=top[y]; } if(x==y){ return Max(ans,findmax(1,num[x],num[x])); } else{ if(dep[x]>dep[y]) swap(x,y); return Max(ans,findmax(1,num[x],num[y])); } } inline long long qsum(int x,int y) { int tx=top[x],ty=top[y]; long long ans=0; while(tx!=ty){ if(dep[tx]>dep[ty]) { swap(x,y); swap(tx,ty); } ans=ans+findsum(1,num[ty],num[y]); y=fa[ty];ty=top[y]; } if(x==y){ return ans+findsum(1,num[x],num[x]); } else{ if(dep[x]>dep[y]) swap(x,y); return ans+findsum(1,num[x],num[y]); } } int main() { n=read(); for(int i=1;i<=n;i++) { head[i]=-1; } for(int i=1;i<n;i++) { int x=read(),y=read(); addedge(x,y); addedge(y,x); } fa[1]=0;dep[1]=1; dfs(1); dfs2(1,1); build(1,1,h); for(int i=1;i<=n;i++) { w[i]=read(); change(1,num[i],w[i]); } q=read(); for(int i=1;i<=q;i++) { cin>>op; int x=read(),y=read(); if(op=="QMAX") printf("%lld\n",qmax(x,y)); else if(op=="QSUM") printf("%lld\n",qsum(x,y)); else change(1,num[x],y); } }