[ZJOI2008]树的统计Count
[ZJOI2008]树的统计Count
2017-09-07
Description
一棵树上有n个节点,编号分别为1到n,每个节点都有一个权值w。我们将以下面的形式来要求你对这棵树完成
一些操作: I. CHANGE u t : 把结点u的权值改为t II. QMAX u v: 询问从点u到点v的路径上的节点的最大权值 I
II. QSUM u v: 询问从点u到点v的路径上的节点的权值和 注意:从点u到点v的路径上的节点包括u和v本身
Input
输入的第一行为一个整数n,表示节点的个数。接下来n – 1行,每行2个整数a和b,表示节点a和节点b之间有
一条边相连。接下来n行,每行一个整数,第i行的整数wi表示节点i的权值。接下来1行,为一个整数q,表示操作
的总数。接下来q行,每行一个操作,以“CHANGE u t”或者“QMAX u v”或者“QSUM u v”的形式给出。
对于100%的数据,保证1<=n<=30000,0<=q<=200000;中途操作中保证每个节点的权值w在-30000到30000之间。
Output
对于每个“QMAX”或者“QSUM”的操作,每行输出一个整数表示要求输出的结果。
Sample Input
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 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
Sample Output
4
1
2
2
10
6
5
6
5
16
1
2
2
10
6
5
6
5
16
这个样例我也是醉了,dfs序和树的点数是一样的,导致dfs出错时完全看不出来.....
现在说树链剖分
所谓树链剖分,就是把一棵树压缩进一颗线段树,把树的每一个点在线段树里分配一个新的标签。
在第一遍dfs的时候找到每一个节点(包括他本身)有多少节点,然后根据节点数分配时重链还是轻链。。
对于每一个点都是先找重链,再轻链...分配id。。扫完根节点之后根据id加入线段树...注意dfs序满足每一颗子树的dfs再统一区间..
现在我们要修改一棵树上的单点,通过pos[i]修改树的时候找到这个标签所对应的地方...区间修改就是先找到链头,然后再线段树分区间加(-|*)
①每一次当前点到链头->跳到另一条链->是否在一条链?
否执行①,是在一条链上,->区间从小到大查询加入答案.>完成查询.;修改同理..
我是先建树,V是按照新id放进线段树里的新权值
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #define ll long long const int maxn=50000+999; using namespace std; int read(){ int an=0,f=1;char ch=getchar(); while(!('0'<=ch&&ch<='9')){if(ch=='-')f=-f;ch=getchar();} while('0'<=ch&&ch<='9'){an=an*10+(ch-'0');ch=getchar();} return f*an; } int v[maxn],cnt,fa[maxn],id,belong[maxn],mx[maxn],f[maxn],pos[maxn],deep[maxn]; ll n,m,size[maxn],V[maxn]; struct saber{ ll sum,ma,l,r; }tr[maxn*4]; bool vis[maxn]; struct sab{ int nex,to; }b[maxn<<1]; void add(int x,int y){ cnt++;b[cnt].nex=f[x]; f[x]=cnt;b[cnt].to=y;} void dfs(int x){ vis[x]=1;size[x]=1; for(int i=f[x];i;i=b[i].nex){ int v=b[i].to; if(!vis[v]){ fa[v]=x; deep[v]=deep[x]+1; dfs(v); size[x]+=size[v]; } } } void dfs2(int x,int chain){ id++;int k=0; mx[x]=pos[x]=id; belong[x]=chain; for(int i=f[x];i;i=b[i].nex){ int v=b[i].to; if(v!=fa[x]&&size[v]>size[k])k=v;} if(k){dfs2(k,chain);mx[x]=max(mx[x],mx[k]);} for(int i=f[x];i;i=b[i].nex){ int v=b[i].to; if(v!=fa[x]&&k!=v)dfs2(v,v); mx[x]=max(mx[x],mx[v]);} } void VV(){for(int i=1;i<=n;i++)V[pos[i]]=v[i];} void build(int k,int l,int r){ tr[k].l=l;tr[k].r=r; if(l==r) {tr[k].ma=V[l];tr[k].sum=V[l];return;} int mid=(l+r)>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum; tr[k].ma=max(tr[k<<1].ma,tr[k<<1|1].ma); } ll askmax(int k,int i,int j){ int l=tr[k].l,r=tr[k].r,mid=(l+r)>>1; if(l==i&&j==r){return tr[k].ma;} if(j<=mid)return askmax(k<<1,i,j); else if(i>mid)return askmax(k<<1|1,i,j); else return max(askmax(k<<1,i,mid),askmax(k<<1|1,mid+1,j)); } ll ask(int k,int i,int j){ int l=tr[k].l,r=tr[k].r,mid=(l+r)>>1; if(l==i&&j==r){return tr[k].sum;} if(j<=mid)return ask(k<<1,i,j); else if(i>mid)return ask(k<<1|1,i,j); else return ask(k<<1,i,mid)+ask(k<<1|1,mid+1,j); } void change(int k,int dang,int da){ int l=tr[k].l,r=tr[k].r,mid=(l+r)>>1; if(l==r&&l==dang){tr[k].sum=da;tr[k].ma=da;return ;} if(dang<=mid)change(k<<1,dang,da); else change(k<<1|1,dang,da); tr[k].sum=tr[k<<1].sum+tr[k<<1|1].sum; tr[k].ma=max(tr[k<<1].ma,tr[k<<1|1].ma); } ll ask(int x,int y){ ll ans=0; while(belong[x]!=belong[y]){ if(deep[belong[x]]<deep[belong[y]])swap(x,y); ans+=ask(1,pos[belong[x]],pos[x]); x=fa[belong[x]]; } if(pos[x]>pos[y])swap(x,y); ans+=ask(1,pos[x],pos[y]); return ans; } ll askmax(int x,int y){ ll ans=-9999999; while(belong[x]!=belong[y]){ if(deep[belong[x]] < deep[belong[y]])swap(x,y); ans=max(ans,askmax(1,pos[belong[x]],pos[x])); x=fa[belong[x]]; } if(pos[x]>pos[y])swap(x,y); ans=max(ans,askmax(1,pos[x],pos[y])); return ans; } int main(){ n=read(); for(int i=1;i<n;i++){ int x,y; x=read();y=read(); add(x,y);add(y,x); } for(int i=1;i<=n;i++)v[i]=read(); dfs(1); dfs2(1,1); VV(); build(1,1,n); m=read(); while(m){m--; int y,z;char kk[10]; cin>>kk;y=read();z=read(); if(kk[0]=='C'){v[y]=z; change(1,pos[y],z);}//把u改成t else if(kk[1]=='M'){ cout<<askmax(y,z)<<endl;}//询问max else { cout<<ask(y,z)<<endl;}//询问和 } return 0; }
by:s_a_b_e_r
这个样例我也是醉了+1
树剖裸题……学习一下树剖w
所谓树剖就是把本来好好的一棵树剖成好几条链,然后一字排开的丢进线段树里,就可以实现每一条链区间O(logn)快速查询。
然后凭借特殊的剖链技巧,可以实现每个点到根节点最多有logn条链,总计查询复杂度O(log2n)
树剖先进行两遍dfs,第一遍求出各点的深度、父节点以及子树(包括这个点)的大小(size)
第二遍开始剖链,每次选一个size最大的儿子继承他的财产重链,剩下的儿子白手起家重新开一条轻链
然后每次查询的时候把两个点靠下的那个向上跳一条链,直到两个点在同一条链上,完成查询
线段树风格不一样导致沟通困难……考虑学一下s酱的线段树?
#include<iostream> #include<cstdio> #define LL long long using namespace std; const int N=30009,inf=1000009; int n,m,cnt,p[N],q,inx; int size[N],dep[N],fa[N],bl[N],pos[N],mx[N]; char s[10]; LL a[N]; struct edge{ int to,nex; }e[N<<1]; struct node{ int l,r; LL sum,ma; }t[N<<2]; void adde(int u,int v) { ++cnt; e[cnt].to=v; e[cnt].nex=p[u]; p[u]=cnt; } void update(int k) { t[k].sum=t[k<<1].sum+t[k<<1|1].sum; t[k].ma=max(t[k<<1].ma,t[k<<1|1].ma); } void dfs1(int u) { size[u]=1; for(int i=p[u];i;i=e[i].nex) { int v=e[i].to; if(v==fa[u])continue; dep[v]=dep[u]+1; fa[v]=u; dfs1(v); size[u]+=size[v]; } } void dfs2(int u,int chain) { int k=0;++inx; pos[u]=mx[u]=inx; bl[u]=chain; for(int i=p[u];i;i=e[i].nex) { int v=e[i].to; if(dep[u]<dep[v]&&size[k]<size[v])k=v; } if(k){dfs2(k,chain);mx[u]=max(mx[u],mx[k]);} for(int i=p[u];i;i=e[i].nex) { int v=e[i].to; if(dep[u]<dep[v]&&k!=v) {dfs2(v,v);mx[u]=max(mx[u],mx[v]);} } } void build(int k,int l,int r) { t[k].l=l,t[k].r=r; if(l==r){return;} int mid=(l+r)>>1; build(k<<1,l,mid); build(k<<1|1,mid+1,r); update(k); } void change(int k,int x,int w) { int l=t[k].l,r=t[k].r; if(l==r){t[k].sum=t[k].ma=w;return;} int mid=(l+r)>>1; if(x<=mid)change(k<<1,x,w); else change(k<<1|1,x,w); update(k); } LL asksum(int k,int L,int R) { int l=t[k].l,r=t[k].r; if(r<L||l>R)return 0; if(L<=l&&R>=r)return t[k].sum; return asksum(k<<1,L,R)+asksum(k<<1|1,L,R); } LL qsum(int u,int v) { LL ans=0; while(bl[u]!=bl[v]) { if(dep[bl[u]]<dep[bl[v]])swap(u,v); ans+=asksum(1,pos[bl[u]],pos[u]); u=fa[bl[u]]; } if(pos[u]>pos[v])swap(u,v); ans+=asksum(1,pos[u],pos[v]); return ans; } LL askma(int k,int L,int R) { int l=t[k].l,r=t[k].r; if(r<L||l>R)return -inf; if(L<=l&&R>=r)return t[k].ma; return max(askma(k<<1,L,R),askma(k<<1|1,L,R)); } LL qma(int u,int v) { LL ans=-inf; while(bl[u]!=bl[v]) { if(dep[bl[u]]<dep[bl[v]])swap(u,v); ans=max(ans,askma(1,pos[bl[u]],pos[u])); u=fa[bl[u]]; } if(pos[u]>pos[v])swap(u,v); ans=max(ans,askma(1,pos[u],pos[v])); return ans; } int main() { cin>>n; for(int i=1;i<n;++i) { int x,y; scanf("%d%d",&x,&y); adde(x,y);adde(y,x); } for(int i=1;i<=n;++i)cin>>a[i]; dfs1(1);dfs2(1,1); build(1,1,n); for(int i=1;i<=n;++i)change(1,pos[i],a[i]); scanf("%d",&q); while(q--) { int a,b; cin>>s; scanf("%d%d",&a,&b); if(s[0]=='C') //change change(1,pos[a],b); else if(s[1]=='M')//qmax printf("%lld\n",qma(a,b)); else //qsum printf("%lld\n",qsum(a,b)); } return 0; }
s:下面是叫bz和luogu的区别,左边luogu,右边bz
w: luogu:你交程序了啊,来我帮你看看,好的你可以AC了
bz:【打出一排RE】你***还想AC?!!