树链剖分与例题
为了解决树上子树修改的问题,我们引入了树的dfs序:在树上dfs时,按dfs到的前后顺序给每个点设置dfn[x]表示x是第dfn[x]到达的
由于子树内的点的dfn连续,我们将点权按照dfn顺序放在序列上,于是子树x全体修改等价于对区间[dfn[x],dfn[x]+siz[x]-1]进行修改,例题如下:
jzyz511 工资查询
区间加减,单点询问,可以用树状数组维护差分数组,则问题转变成单点修改,求前缀和,可以用树状数组解决。用线段树也可,因为马上要用到很多次线段树。
有一个小坑点是修改工资时不给自己修改,只给子树内其他点修改,因此需要特判叶子的情况,修改区间也从[dfn[x],dfn[x]+siz[x]-1]变为了[dfn[x]+1,dfn[x]+siz[x]-1]
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=500010; int lowbit(int x){return x&(-x);} ll read(){ll x;scanf("%lld",&x);return x;} ll c[N],b[N],a[N]; int n,l[N],r[N],tot,m,dfn[N]; vector<int>e[N]; void add(int x,int v) { while(x<=n) { c[x]+=v; x+=lowbit(x); } } ll ask(int x) { ll t=0; while(x) { t+=c[x]; x-=lowbit(x); } return t; } void dfs(int x) { dfn[x]=++tot; b[tot]=a[x]; l[x]=tot; for(auto y:e[x]) { dfs(y); } r[x]=tot; } int main() { freopen("place.in","r",stdin); freopen("place.out","w",stdout); n=read();m=read(); a[1]=read(); for(int i=2;i<=n;i++) { a[i]=read(); e[read()].push_back(i); } dfs(1); for(int i=n;i>=1;i--) { b[i]=b[i]-b[i-1]; add(i,b[i]); } for(;m;m--) { char c; cin>>c; if(c=='p') { int x=read(),v=read(); add(l[x]+1,v); add(r[x]+1,-v); } else { int x=read(); cout<<ask(dfn[x])<<"\n"; } } }
为了解决树上链的修改、询问,我们引入重儿子的定义:x的所有儿子中siz最大的为x的重儿子,其他的称为轻儿子。x和自己的重儿子连边称为重边,重边们组成重链;轻儿子会再开一个重链,和x的连边称为轻边。
这需要两次dfs,第一次计算siz,第二次确定重儿子son,每个点所在重链的起始节点bel,dfs序dfn。特别是dfn需要在第二次先走重儿子,再走轻儿子时计算,不能第一次随意走,计算siz时就确定dfn。
树链剖分后,同一重链的节点们的dfs序是连续的,同时也满足同一子树的节点们dfs序连续,结合线段树,主席树等数据结构可以处理链的修改和子树修改了。
jzyz576 LCA模板
树链剖分后也可以用于求lca:比较bel[x]和bel[y]的深度,谁更大就跳谁,直到bel[x]==bel[y],此时比较siz[x]和siz[y]或者dep[x]和dep[y]即可求得lca。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=500010; int lowbit(int x){return x&(-x);} ll read(){ll x;scanf("%lld",&x);return x;} int n,m,dep[N],siz[N],bel[N],fa[N]; vector<int>e[N]; void dfs(int x) { siz[x]=1; for(auto y:e[x]){ if(y==fa[x]) continue; dep[y]=dep[x]+1; fa[y]=x; dfs(y); siz[x]+=siz[y]; } } void dfs(int x,int chain) { bel[x]=chain; int k=0; for(auto y:e[x]) { if(y==fa[x]) continue; if(siz[y]>siz[k]) k=y; } if(k)//如果不是叶子 dfs(k,chain); for(auto y:e[x]) { if(y==fa[x]||y==k) continue; dfs(y,y); } } int lca(int x,int y) { while(bel[x]!=bel[y]) { if(dep[bel[x]]>dep[bel[y]]) x=fa[bel[x]]; else y=fa[bel[y]]; } // if(bel[x]==bel[y]) if(siz[x]>siz[y]) return x; return y; } int main() { n=read();m=read(); for(int i=2;i<=n;i++) { int x=read(),y=read(); e[x].push_back(y); e[y].push_back(x); } dfs(1); dfs(1,1); for(;m;m--) cout<<lca(read(),read())<<'\n'; }
jzyz luoguP5903 树上 K 级祖先
求k级祖先,由于重链上dfs序连续,因此可以用dep[x]-dep[bel[x]]<k检查重链起始位置距离x是否大于等于k。如果小于,应该跳到重链起始位置再往上跳一下:x=fa[bel[x]],跳轻链跳到下一重链上,直到k级祖先和x在同一个重链上。
在同一个重链上了之后,k级祖先的dfs序也就有了,是dfn[x]-k。有了dfs序,从dfs序反向可以得到k级祖先。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=500010; int lowbit(int x){return x&(-x);} ll read(){ll x;scanf("%lld",&x);return x;} int n,dep[N],siz[N],bel[N],fa[N],dfn[N],nfd[N],tot; vector<int>e[N]; void dfs(int x) { siz[x]=1; for(auto y:e[x]){ dep[y]=dep[x]+1; fa[y]=x; dfs(y); siz[x]+=siz[y]; } } void dfs(int x,int chain) { bel[x]=chain; dfn[x]=++tot; nfd[tot]=x; int k=0; for(auto y:e[x]) { if(siz[y]>siz[k]) k=y; } if(k)//如果不是叶子 dfs(k,chain); for(auto y:e[x]) { if(y==k) continue; dfs(y,y); } } int getx(int x,int k) { while(x!=0&&dep[x]-dep[bel[x]]<k) { k-=dep[x]-dep[bel[x]]+1; x=fa[bel[x]]; } if(x==0) return 0; int t=dfn[x]-k; return nfd[t]; } int ans[5000010]; unsigned int s; unsigned int get(unsigned int x) { x^=x<<13; x^=x>>17; x^=x<<5; return s=x; } int main() { n=read();int q=read();s=read(); int rt; for(int i=1;i<=n;i++) { int f=read(); if(f==0) rt=i; else e[f].push_back(i); } dep[rt]=1; dfs(rt); dfs(rt,rt); ll sum=0; for(ll i=1;i<=q;i++) { int x=(get(s)^ans[i-1])%n+1; int k=(get(s)^ans[i-1])%dep[x]; ans[i]=getx(x,k); sum=sum^(i*ans[i]); } cout<<sum; }
jzyz1525 严格次小生成树
先构造一个最小生成树
枚举没选上的边x,y,w,看最小生成树上的x到y的路径上的边权们,如果最大值=w,我们应该用x,y,w替换次大值。否则应该替换掉最大值。
因此需要m-n+1次询问路径最大值,严格次大值,使用线段树即可。注意严格次大值。
蓝书上使用了倍增lca来解决,推荐大家写一下树链剖分版本。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=100010; int lowbit(int x){return x&(-x);} ll read(){ll x;scanf("%lld",&x);return x;} int n,m,dep[N],siz[N],bel[N],fa[N],dfn[N],tot,a[N]; vector<pair<int,int>>e[N]; pair<int,int>c[N*4]; int get(int x) { return fa[x]==x?x:fa[x]=get(fa[x]); } void dfs(int x) { siz[x]=1; for(auto [y,w]:e[x]){ if(y==fa[x]) continue; dep[y]=dep[x]+1; fa[y]=x; dfs(y); siz[x]+=siz[y]; } } void dfs(int x,int chain) { dfn[x]=++tot;bel[x]=chain; int k=0; for(auto [y,w]:e[x]) { if(y==fa[x])continue; if(siz[y]>siz[k]) k=y; } if(k)//如果不是叶子 dfs(k,chain); for(auto [y,w]:e[x]) { if(y==fa[x]||y==k)continue; dfs(y,y); } } pair<int,int>merge(pair<int,int>a,pair<int,int>b) { int cc[]={a.first,a.second,b.first,b.second}; sort(cc,cc+4); for(int i=2;i>=0;i--) if(cc[i]!=cc[3]) return {cc[3],cc[i]}; } pair<int,int> ask(int x,int l,int r,int tl,int tr) { if(tl<=l&&r<=tr) return c[x]; pair<int,int>t={-1e9,-1e9}; int mid=(l+r)/2; if(tl<=mid) t=merge(t,ask(x*2,l,mid,tl,tr)); if(tr>mid) t=merge(t,ask(x*2+1,mid+1,r,tl,tr)); return t; } pair<int,int> askmaxx(int x,int y) { pair<int,int>maxx={-1e9,-1e9}; while(bel[x]!=bel[y]) { if(dep[bel[x]]>dep[bel[y]]) { maxx=merge(maxx,ask(1,1,n,dfn[bel[x]],dfn[x])); x=fa[bel[x]]; } else { maxx=merge(maxx,ask(1,1,n,dfn[bel[y]],dfn[y])); y=fa[bel[y]]; } } if(siz[x]>siz[y]) maxx=merge(maxx,ask(1,1,n,dfn[x]+1,dfn[y])); else if(siz[x]<siz[y]) maxx=merge(maxx,ask(1,1,n,dfn[y]+1,dfn[x])); return maxx; } void add(int x,int l,int r,int d,ll v) { if(l==r) { c[x]={v,-1e9}; return ; } int mid=(l+r)/2; if(d<=mid) add(x*2,l,mid,d,v); else add(x*2+1,mid+1,r,d,v); c[x]=max(c[x*2],c[x*2+1]); } struct edge { int x,y,w,flag; friend bool operator <(edge a,edge b) { return a.w<b.w; } }ee[300010]; ll sum; void kru() { sort(ee+1,ee+1+m); for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=m;i++) { if(get(ee[i].x)==get(ee[i].y)) continue; ee[i].flag=1;//用上了这条边 sum+=ee[i].w; e[ee[i].x].push_back({ee[i].y,ee[i].w}); e[ee[i].y].push_back({ee[i].x,ee[i].w}); fa[get(ee[i].x)]=get(ee[i].y); } memset(fa,0,sizeof(fa)); } int main() { freopen("test.in","r",stdin); freopen("test.out","w",stdout); n=read();m=read(); for(int i=1;i<=m;i++) ee[i]={read(),read(),read()}; kru(); dfs(1); dfs(1,1); for(int i=2;i<=n;i++) for(auto [y,w]:e[i]) if(y==fa[i]) add(1,1,n,dfn[i],w);//i到父亲的边 ll ans=1e18; for(int i=1;i<=m;i++) { if(ee[i].flag) continue; pair<int,int>t=askmaxx(ee[i].x,ee[i].y); if(t.first!=ee[i].w) ans=min(ans,sum+ee[i].w-t.first); else ans=min(ans,sum+ee[i].w-t.second); } cout<<ans; }
CF733F Drivers Dissatisfaction
首先一个结论是一定对着一个边狠狠地花钱,没有必要在一条以上的边上花钱。
这样一条边花完钱后变成了w-S/c,非常地好做
跑完最小生成树跑树链剖分,最后枚举每条边,如果他在最小生成树里,最终收益是sum-S/c。如果不在最小生成树里,他可以替代最小生成树上x到y的最大边,最终收益是sum-maxx+w-S/c。
最后输出方案,是否需要维护最大值的同时维护最大值的边的编号呢?其实没有必要,记录一下当前最小值是修改了pos号边得来的,最后修改一下pos号边再跑一次克鲁斯卡尔输出答案即可。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=200010; int lowbit(int x){return x&(-x);} ll read(){ll x;scanf("%lld",&x);return x;} int n,m,dep[N],siz[N],bel[N],fa[N],dfn[N],tot,a[N]; vector<pair<int,int>>e[N]; int maxx[N*4]; int get(int x) { return fa[x]==x?x:fa[x]=get(fa[x]); } void dfs(int x) { siz[x]=1; for(auto [y,w]:e[x]){ if(y==fa[x]) continue; dep[y]=dep[x]+1; fa[y]=x; dfs(y); siz[x]+=siz[y]; } } void dfs(int x,int chain) { dfn[x]=++tot;bel[x]=chain; int k=0; for(auto [y,w]:e[x]) { if(y==fa[x])continue; if(siz[y]>siz[k]) k=y; } if(k)//如果不是叶子 dfs(k,chain); for(auto [y,w]:e[x]) { if(y==fa[x]||y==k)continue; dfs(y,y); } } int ask(int x,int l,int r,int tl,int tr) { if(tl<=l&&r<=tr) return maxx[x]; int t=-1e9; int mid=(l+r)/2; if(tl<=mid) t=max(t,ask(x*2,l,mid,tl,tr)); if(tr>mid) t=max(t,ask(x*2+1,mid+1,r,tl,tr)); return t; } int askmaxx(int x,int y) { int maxx=-1e9; while(bel[x]!=bel[y]) { if(dep[bel[x]]>dep[bel[y]]) { maxx=max(maxx,ask(1,1,n,dfn[bel[x]],dfn[x])); x=fa[bel[x]]; } else { maxx=max(maxx,ask(1,1,n,dfn[bel[y]],dfn[y])); y=fa[bel[y]]; } } if(siz[x]>siz[y]) maxx=max(maxx,ask(1,1,n,dfn[x]+1,dfn[y])); else if(siz[x]<siz[y]) maxx=max(maxx,ask(1,1,n,dfn[y]+1,dfn[x])); return maxx; } void add(int x,int l,int r,int d,int v) { if(l==r) { maxx[x]=v; return ; } int mid=(l+r)/2; if(d<=mid) add(x*2,l,mid,d,v); else add(x*2+1,mid+1,r,d,v); maxx[x]=max(maxx[x*2],maxx[x*2+1]); } struct edge { int x,y,w,c,flag,i; friend bool operator <(edge a,edge b) { return a.w<b.w; } }ee[300010]; ll sum; void kru() { sort(ee+1,ee+1+m); for(int i=1;i<=n;i++) fa[i]=i; for(int i=1;i<=m;i++) { if(get(ee[i].x)==get(ee[i].y)) continue; ee[i].flag=1;//用上了这条边 sum+=ee[i].w; e[ee[i].x].push_back({ee[i].y,ee[i].w}); e[ee[i].y].push_back({ee[i].x,ee[i].w}); fa[get(ee[i].x)]=get(ee[i].y); } memset(fa,0,sizeof(fa)); } int main() { n=read();m=read(); for(int i=1;i<=m;i++) { ee[i].w=read(); ee[i].i=i; } for(int i=1;i<=m;i++) ee[i].c=read(); for(int i=1;i<=m;i++) { ee[i].x=read(); ee[i].y=read(); } kru(); dfs(1); dfs(1,1); for(int i=2;i<=n;i++) for(auto [y,w]:e[i]) if(y==fa[i]) add(1,1,n,dfn[i],w);//i到父亲的边 int S=read(); ll ans=1e18,pos=0; for(int i=1;i<=m;i++) { ll v; if(ee[i].flag) { v=sum-S/ee[i].c; if(v<ans) ans=v,pos=i; } else { v=sum-askmaxx(ee[i].x,ee[i].y)+ee[i].w-S/ee[i].c; if(v<ans) ans=v,pos=i; } ee[i].flag=0; } ee[pos].w-=S/ee[pos].c; kru(); cout<<ans<<'\n'; for(int i=1;i<=m;i++) if(ee[i].flag) cout<<ee[i].i<<' '<<ee[i].w<<'\n'; }
BZOJ2238 Mst
先不管无解了,看看有解的时候怎么办
和上一题类似,爬完最小生成树跑树链剖分
注意到如果被删的边是非树边,答案依然是sum。否则答案是sum-ee[i].w+所有覆盖(ee[i].x,ee[i].y)这条边的非树边边权的最小值
因此拿着非树边更新树上路径的最小值即可。
如果最小生成树没跑出来,说明并非联通,直接q次循环每次输出Not connected即可
否则,看看这条边的最小值,如果是0x3f3f3f3f说明没有被更新过,意思是没有非树边覆盖这条边,删了这条边就不了联通了,因此输出Not connected
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=100010; int lowbit(int x){return x&(-x);} ll read(){ll x;scanf("%lld",&x);return x;} int n,m,dep[N],siz[N],bel[N],fa[N],dfn[N],tot,a[N]; vector<pair<int,int>>e[N]; int c[N*4]; int get(int x) { return fa[x]==x?x:fa[x]=get(fa[x]); } void dfs(int x) { siz[x]=1; for(auto [y,w]:e[x]){ if(y==fa[x]) continue; dep[y]=dep[x]+1; fa[y]=x; dfs(y); siz[x]+=siz[y]; } } void dfs(int x,int chain) { dfn[x]=++tot;bel[x]=chain; int k=0; for(auto [y,w]:e[x]) { if(y==fa[x])continue; if(siz[y]>siz[k]) k=y; } if(k)//如果不是叶子 dfs(k,chain); for(auto [y,w]:e[x]) { if(y==fa[x]||y==k)continue; dfs(y,y); } } int ask(int x,int l,int r,int d) { if(l==r) return c[x]; int mid=(l+r)/2; if(d<=mid) return min(c[x],ask(x*2,l,mid,d)); else return min(c[x],ask(x*2+1,mid+1,r,d)); } void add(int x,int l,int r,int tl,int tr,int v) { if(tl<=l&&r<=tr) { c[x]=min(c[x],v); return ; } int mid=(l+r)/2; if(tl<=mid) add(x*2,l,mid,tl,tr,v); if(tr>mid) add(x*2+1,mid+1,r,tl,tr,v); } void lca_add(int x,int y,int v)//询问最小值 { while(bel[x]!=bel[y]) { if(dep[bel[x]]>dep[bel[y]]) { add(1,1,n,dfn[bel[x]],dfn[x],v); x=fa[bel[x]]; } else { add(1,1,n,dfn[bel[y]],dfn[y],v); y=fa[bel[y]]; } } if(siz[x]>siz[y]) add(1,1,n,dfn[x]+1,dfn[y],v); else if(siz[x]<siz[y]) add(1,1,n,dfn[y]+1,dfn[x],v); } struct edge { int x,y,w,i,flag; friend bool operator <(edge a,edge b) { return a.w<b.w; } }ee[300010]; ll sum; void kru() { sort(ee+1,ee+1+m); for(int i=1;i<=n;i++) fa[i]=i; int cntt=0; for(int i=1;i<=m;i++) { if(get(ee[i].x)==get(ee[i].y)) continue; cntt++; ee[i].flag=1;//用上了这条边 sum+=ee[i].w; e[ee[i].x].push_back({ee[i].y,ee[i].w}); e[ee[i].y].push_back({ee[i].x,ee[i].w}); fa[get(ee[i].x)]=get(ee[i].y); } memset(fa,0,sizeof(fa)); if(cntt!=n-1) { for(int q=read();q;q--) cout<<"Not connected\n"; exit(0); } } bool i_(edge a,edge b) { return a.i<b.i; } int main() { // freopen("test.in","r",stdin); // freopen("test.out","w",stdout); n=read();m=read(); for(int i=1;i<=m;i++) ee[i]={read(),read(),read(),i}; kru(); dfs(1); dfs(1,1); memset(c,0x3f,sizeof(c)); sort(ee+1,ee+1+m,i_); for(int i=1;i<=m;i++) { if(ee[i].flag) continue;//被用了,我的答案一会再求 //没被用,答案是sum,可以更新别人的答案 lca_add(ee[i].x,ee[i].y,ee[i].w); } for(int q=read();q;q--) { int t=read(); if(ee[t].flag) { int x=ee[t].x,y=ee[t].y; if(siz[x]>siz[y]) swap(x,y); if(ask(1,1,n,dfn[x])==0x3f3f3f3f) cout<<"Not connected\n"; else cout<<sum-ee[t].w+ask(1,1,n,dfn[x])<<'\n'; } else cout<<sum<<'\n'; } }
jzyz593 树分治例3 spoj375 qtree
在这里讲一下树链剖分怎么处理边的事情:
我的做法是把xyw放在sizx和sizy较小的地方,谁小放在谁那里,也就是儿子那边。这样修改的时候对着线段树上dfn[x]修改即可。询问的时候跳x/跳y依然是ask(1,1,n,dfn[bel[x]],dfn[x])/ask(1,1,n,dfn[bel[y]],dfn[y])。只有最后在同一个重链的时候,比如x在y的上面,你需要对区间[dfn[x]+1,dfn[y]]取max,不能算上dfn[x],因为这个对应的边是lca到fa[lca]的。如果最后x=y了,则应该不取max。
这题多组数据,数组初始化这块需要注意
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=10010; int lowbit(int x){return x&(-x);} ll read(){ll x;scanf("%lld",&x);return x;} int n,dep[N],siz[N],bel[N],fa[N],dfn[N],tot; vector<pair<int,int>>e[N]; struct node { int x,y,w,i; }ee[N]; int maxx[N*4]; void add(int x,int l,int r,int d,int v) { if(l==r){ maxx[x]=v; return ; } int mid=(l+r)/2; if(d<=mid) add(x*2,l,mid,d,v); else add(x*2+1,mid+1,r,d,v); maxx[x]=max(maxx[x*2],maxx[x*2+1]); } int ask(int x,int l,int r,int tl,int tr) { if(tl<=l&&r<=tr) return maxx[x]; int mid=(l+r)/2,t=0; if(tl<=mid) t=ask(x*2,l,mid,tl,tr); if(tr>mid) t=max(t,ask(x*2+1,mid+1,r,tl,tr)); return t; } void dfs(int x) { siz[x]=1; for(auto [y,w]:e[x]){ if(y==fa[x]) continue; dep[y]=dep[x]+1; fa[y]=x; dfs(y); siz[x]+=siz[y]; } } void dfs(int x,int chain) { bel[x]=chain; dfn[x]=++tot; int k=0; for(auto [y,w]:e[x]) { if(fa[x]==y){ add(1,1,n,dfn[x],w);//单点修改 continue; } if(siz[y]>siz[k]) k=y; } if(k)//如果不是叶子 dfs(k,chain); for(auto [y,w]:e[x]) { if(y==k||y==fa[x]) continue; dfs(y,y); } } int lca_max(int x,int y) { int t=0; while(bel[x]!=bel[y]) { if(dep[bel[x]]>dep[bel[y]]) { t=max(t,ask(1,1,n,dfn[bel[x]],dfn[x])); x=fa[bel[x]]; } else { t=max(t,ask(1,1,n,dfn[bel[y]],dfn[y])); y=fa[bel[y]]; } } if(siz[x]>siz[y]) t=max(t,ask(1,1,n,dfn[x]+1,dfn[y])); else if(siz[x]<siz[y]) t=max(t,ask(1,1,n,dfn[y]+1,dfn[x])); return t; } void work() { n=read(); for(int i=1;i<=n;i++) e[i].clear(); tot=0; for(int i=1;i<n;i++) { int x=read(),y=read(),w=read(); ee[i]={x,y,w,i}; e[x].push_back({y,w}); e[y].push_back({x,w}); dep[i]=0; } memset(maxx,0,n*sizeof(int)); dep[1]=read(); dfs(1); dfs(1,1); for(int i=1;i<n;i++) { if(dep[ee[i].x]<dep[ee[i].y]) swap(ee[i].x,ee[i].y); } while(1) { string s; cin>>s; if(s=="DONE") return ; if(s=="CHANGE") { int i=read(),w=read(); int x=ee[i].x; add(1,1,n,dfn[x],w); } else cout<<lca_max(read(),read())<<'\n'; } } int main() { // freopen("QTREE1.in","r",stdin); // freopen("QTREE1.out","w",stdout); for(int t=read();t;t--) work(); }
BZOJ1984. 月下“毛景树”
还是边的事情,继续像上几题这样处理
区间覆盖,区间加,区间询问max,我们同时维护区间覆盖懒标记lz,区间加懒标记ad,区间最大值maxx
遇到修改这样处理即可:

#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=100010; int lowbit(int x){return x&(-x);} ll read(){ll x;scanf("%lld",&x);return x;} int n,dep[N],siz[N],bel[N],fa[N],dfn[N],tot; vector<pair<int,int>>e[N]; struct node { int x,y,w; }ee[N]; int maxx[N*4],lz[N*4],ad[N*4]; void pushdown(int x) { if(lz[x]==-1)//没有区间覆盖 { if(ad[x]==0)//不需要push return ; if(lz[x*2]==-1) ad[x*2]+=ad[x]; else lz[x*2]+=ad[x]; if(lz[x*2+1]==-1) ad[x*2+1]+=ad[x]; else lz[x*2+1]+=ad[x]; maxx[x*2]+=ad[x]; maxx[x*2+1]+=ad[x]; } else { lz[x*2]=lz[x*2+1]=lz[x]; ad[x*2]=ad[x*2+1]=0; maxx[x*2]=maxx[x*2+1]=lz[x]; } lz[x]=-1; ad[x]=0; } void add(int x,int l,int r,int tl,int tr,int v)//区间加 { if(tl<=l&&r<=tr) { if(lz[x]==-1) ad[x]+=v; else lz[x]+=v; maxx[x]+=v; return ; } pushdown(x); int mid=(l+r)/2; if(tl<=mid) add(x*2,l,mid,tl,tr,v); if(tr>mid) add(x*2+1,mid+1,r,tl,tr,v); maxx[x]=max(maxx[x*2],maxx[x*2+1]); } void cover(int x,int l,int r,int tl,int tr,int w)//区间覆盖 { if(tl<=l&&r<=tr) { maxx[x]=lz[x]=w; ad[x]=0; return ; } pushdown(x); int mid=(l+r)/2; if(tl<=mid) cover(x*2,l,mid,tl,tr,w); if(tr>mid) cover(x*2+1,mid+1,r,tl,tr,w); maxx[x]=max(maxx[x*2],maxx[x*2+1]); } int ask(int x,int l,int r,int tl,int tr)//区间询问最大值 { if(tl<=l&&r<=tr) return maxx[x]; pushdown(x); int mid=(l+r)/2,t=0; if(tl<=mid) t=ask(x*2,l,mid,tl,tr); if(tr>mid) t=max(t,ask(x*2+1,mid+1,r,tl,tr)); return t; } void dfs(int x) { siz[x]=1; for(auto [y,w]:e[x]){ if(y==fa[x]) continue; dep[y]=dep[x]+1; fa[y]=x; dfs(y); siz[x]+=siz[y]; } } void dfs(int x,int chain) { bel[x]=chain; dfn[x]=++tot; int k=0; for(auto [y,w]:e[x]) { if(fa[x]==y){ add(1,1,n,dfn[x],dfn[x],w);//单点修改 continue; } if(siz[y]>siz[k]) k=y; } if(k)//如果不是叶子 dfs(k,chain); for(auto [y,w]:e[x]) { if(y==k||y==fa[x]) continue; dfs(y,y); } } void lca_cover(int x,int y,int w) { while(bel[x]!=bel[y]) { if(dep[bel[x]]>dep[bel[y]]) { cover(1,1,n,dfn[bel[x]],dfn[x],w); x=fa[bel[x]]; } else { cover(1,1,n,dfn[bel[y]],dfn[y],w); y=fa[bel[y]]; } } if(siz[x]>siz[y]) cover(1,1,n,dfn[x]+1,dfn[y],w); else if(siz[x]<siz[y]) cover(1,1,n,dfn[y]+1,dfn[x],w); } void lca_add(int x,int y,int w) { while(bel[x]!=bel[y]) { if(dep[bel[x]]>dep[bel[y]]) { add(1,1,n,dfn[bel[x]],dfn[x],w); x=fa[bel[x]]; } else { add(1,1,n,dfn[bel[y]],dfn[y],w); y=fa[bel[y]]; } } if(siz[x]>siz[y]) add(1,1,n,dfn[x]+1,dfn[y],w); else if(siz[x]<siz[y]) add(1,1,n,dfn[y]+1,dfn[x],w); } int lca_max(int x,int y) { int t=0; while(bel[x]!=bel[y]) { if(dep[bel[x]]>dep[bel[y]]) { t=max(t,ask(1,1,n,dfn[bel[x]],dfn[x])); x=fa[bel[x]]; } else { t=max(t,ask(1,1,n,dfn[bel[y]],dfn[y])); y=fa[bel[y]]; } } if(siz[x]>siz[y]) t=max(t,ask(1,1,n,dfn[x]+1,dfn[y])); else if(siz[x]<siz[y]) t=max(t,ask(1,1,n,dfn[y]+1,dfn[x])); return t; } int main() { memset(lz,-1,sizeof(lz)); n=read(); for(int i=1;i<n;i++) { int x=read(),y=read(),w=read(); ee[i]={x,y,w}; e[x].push_back({y,w}); e[y].push_back({x,w}); } dep[1]=1; dfs(1); dfs(1,1); for(int i=1;i<n;i++) if(dep[ee[i].x]<dep[ee[i].y]) swap(ee[i].x,ee[i].y); while(1) { string s; cin>>s; if(s=="Stop") break ; if(s=="Change") { int k=read(),w=read(); cover(1,1,n,dfn[ee[k].x],dfn[ee[k].x],w); } else if(s=="Cover") { int x=read(),y=read(),w=read(); lca_cover(x,y,w); } else if(s=="Add") { int x=read(),y=read(),w=read(); lca_add(x,y,w); } else cout<<lca_max(read(),read())<<'\n'; } }
BZOJ2157 [国家集训队] 旅游
是大杂烩,写起来不是很难
依然是路径的问题,单点修改,区间取反,区间求和,区间最大值,区间最小值
取反的时候,区间和当然是加负号。maxx和minn我是这样做的:
minn[x]=-minn[x];
maxx[x]=-maxx[x];
swap(minn[x],maxx[x]);
感觉很对!
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=200010; int lowbit(int x){return x&(-x);} ll read(){ll x;scanf("%lld",&x);return x;} int n,dep[N],siz[N],bel[N],fa[N],dfn[N],tot; vector<pair<int,int>>e[N]; struct node { int x,y,w; }ee[N]; int maxx[N*4],minn[N*4],lz[N*4],sum[N*4]; void NOT(int x)//线段树节点x取反 { lz[x]^=1; sum[x]=-sum[x]; minn[x]=-minn[x]; maxx[x]=-maxx[x]; swap(minn[x],maxx[x]); } void pushdown(int x) { if(lz[x]) { NOT(x*2); NOT(x*2+1); lz[x]=0; } } void add(int x,int l,int r,int d,int v)//单点修改 { if(l==r) { minn[x]=maxx[x]=sum[x]=v; return ; } pushdown(x); int mid=(l+r)/2; if(d<=mid) add(x*2,l,mid,d,v); else add(x*2+1,mid+1,r,d,v); maxx[x]=max(maxx[x*2],maxx[x*2+1]); minn[x]=min(minn[x*2],minn[x*2+1]); sum[x]=sum[x*2]+sum[x*2+1]; } void NOT(int x,int l,int r,int tl,int tr)//区间取反 { if(tl<=l&&r<=tr) { NOT(x); return ; } pushdown(x); int mid=(l+r)/2; if(tl<=mid) NOT(x*2,l,mid,tl,tr); if(tr>mid) NOT(x*2+1,mid+1,r,tl,tr); maxx[x]=max(maxx[x*2],maxx[x*2+1]); minn[x]=min(minn[x*2],minn[x*2+1]); sum[x]=sum[x*2]+sum[x*2+1]; } int asksum(int x,int l,int r,int tl,int tr)//区间询问最大值 { if(tl<=l&&r<=tr) return sum[x]; pushdown(x); int mid=(l+r)/2,t=0; if(tl<=mid) t+=asksum(x*2,l,mid,tl,tr); if(tr>mid) t+=asksum(x*2+1,mid+1,r,tl,tr); return t; } int askmin(int x,int l,int r,int tl,int tr) { if(tl<=l&&r<=tr) return minn[x]; pushdown(x); int mid=(l+r)/2,t=1e9; if(tl<=mid) t=askmin(x*2,l,mid,tl,tr); if(tr>mid) t=min(t,askmin(x*2+1,mid+1,r,tl,tr)); return t; } int askmax(int x,int l,int r,int tl,int tr) { if(tl<=l&&r<=tr) return maxx[x]; pushdown(x); int mid=(l+r)/2,t=-1e9; if(tl<=mid) t=askmax(x*2,l,mid,tl,tr); if(tr>mid) t=max(t,askmax(x*2+1,mid+1,r,tl,tr)); return t; } void dfs(int x) { siz[x]=1; for(auto [y,w]:e[x]){ if(y==fa[x]) continue; dep[y]=dep[x]+1; fa[y]=x; dfs(y); siz[x]+=siz[y]; } } void dfs(int x,int chain) { bel[x]=chain; dfn[x]=++tot; int k=0; for(auto [y,w]:e[x]) { if(fa[x]==y){ add(1,1,n,dfn[x],w);//单点修改 continue; } if(siz[y]>siz[k]) k=y; } if(k)//如果不是叶子 dfs(k,chain); for(auto [y,w]:e[x]) { if(y==k||y==fa[x]) continue; dfs(y,y); } } void lca_not(int x,int y) { while(bel[x]!=bel[y]) { if(dep[bel[x]]>dep[bel[y]]) { NOT(1,1,n,dfn[bel[x]],dfn[x]); x=fa[bel[x]]; } else { NOT(1,1,n,dfn[bel[y]],dfn[y]); y=fa[bel[y]]; } } if(siz[x]>siz[y]) NOT(1,1,n,dfn[x]+1,dfn[y]); else if(siz[x]<siz[y]) NOT(1,1,n,dfn[y]+1,dfn[x]); } int lca_sum(int x,int y) { int t=0; while(bel[x]!=bel[y]) { if(dep[bel[x]]>dep[bel[y]]) { t+=asksum(1,1,n,dfn[bel[x]],dfn[x]); x=fa[bel[x]]; } else { t+=asksum(1,1,n,dfn[bel[y]],dfn[y]); y=fa[bel[y]]; } } if(siz[x]>siz[y]) t+=asksum(1,1,n,dfn[x]+1,dfn[y]); else if(siz[x]<siz[y]) t+=asksum(1,1,n,dfn[y]+1,dfn[x]); return t; } int lca_min(int x,int y) { int t=1e9; while(bel[x]!=bel[y]) { if(dep[bel[x]]>dep[bel[y]]) { t=min(t,askmin(1,1,n,dfn[bel[x]],dfn[x])); x=fa[bel[x]]; } else { t=min(t,askmin(1,1,n,dfn[bel[y]],dfn[y])); y=fa[bel[y]]; } } if(siz[x]>siz[y]) t=min(t,askmin(1,1,n,dfn[x]+1,dfn[y])); else if(siz[x]<siz[y]) t=min(t,askmin(1,1,n,dfn[y]+1,dfn[x])); return t; } int lca_max(int x,int y) { int t=-1e9; while(bel[x]!=bel[y]) { if(dep[bel[x]]>dep[bel[y]]) { t=max(t,askmax(1,1,n,dfn[bel[x]],dfn[x])); x=fa[bel[x]]; } else { t=max(t,askmax(1,1,n,dfn[bel[y]],dfn[y])); y=fa[bel[y]]; } } if(siz[x]>siz[y]) t=max(t,askmax(1,1,n,dfn[x]+1,dfn[y])); else if(siz[x]<siz[y]) t=max(t,askmax(1,1,n,dfn[y]+1,dfn[x])); return t; } int main() { n=read(); for(int i=1;i<n;i++) { int x=read()+1,y=read()+1,w=read(); ee[i]={x,y,w}; e[x].push_back({y,w}); e[y].push_back({x,w}); } dep[1]=1; dfs(1); dfs(1,1); for(int i=1;i<n;i++) if(dep[ee[i].x]<dep[ee[i].y]) swap(ee[i].x,ee[i].y); int m=read(); for(int m=read();m;m--) { string s; cin>>s; if(s=="C") { int k=read(),w=read(); add(1,1,n,dfn[ee[k].x],w); } else if(s=="N") lca_not(read()+1,read()+1); else if(s=="SUM") cout<<lca_sum(read()+1,read()+1)<<'\n'; else if(s=="MAX") cout<<lca_max(read()+1,read()+1)<<'\n'; else if(s=="MIN") cout<<lca_min(read()+1,read()+1)<<'\n'; } }
jzyz585 [zjoi2008] 树的统计
单点修改,询问路径权值最大值,询问路径权值和,线段树即可
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=500010; int lowbit(int x){return x&(-x);} ll read(){ll x;scanf("%lld",&x);return x;} int n,m,dep[N],siz[N],bel[N],fa[N],dfn[N],tot; vector<int>e[N]; pair<int,int>c[N]; void dfs(int x) { siz[x]=1; for(auto y:e[x]){ if(y==fa[x]) continue; dep[y]=dep[x]+1; fa[y]=x; dfs(y); siz[x]+=siz[y]; } } void dfs(int x,int chain) { dfn[x]=++tot; bel[x]=chain; int k=0; for(auto y:e[x]) { if(y==fa[x]) continue; if(siz[y]>siz[k]) k=y; } if(k)//如果不是叶子 dfs(k,chain); for(auto y:e[x]) { if(y==fa[x]||y==k) continue; dfs(y,y); } } pair<int,int>ask(int x,int l,int r,int tl,int tr) { assert(tl<=tr); if(tl<=l&&r<=tr) return c[x]; pair<int,int>t={-4e4,0}; int mid=(l+r)/2; if(tl<=mid) { auto tt=ask(x*2,l,mid,tl,tr); t.first=max(tt.first,t.first); t.second+=tt.second; } if(tr>mid) { auto tt=ask(x*2+1,mid+1,r,tl,tr); t.first=max(tt.first,t.first); t.second+=tt.second; } return t; } int askmax(int x,int y) { int maxx=-5e4; while(bel[x]!=bel[y]) { if(dep[bel[x]]>dep[bel[y]]) { maxx=max(maxx,ask(1,1,n,dfn[bel[x]],dfn[x]).first); x=fa[bel[x]]; } else{ maxx=max(maxx,ask(1,1,n,dfn[bel[y]],dfn[y]).first); y=fa[bel[y]]; } } if(siz[x]>siz[y]) maxx=max(maxx,ask(1,1,n,dfn[x],dfn[y]).first); else maxx=max(maxx,ask(1,1,n,dfn[y],dfn[x]).first); return maxx; } int asksum(int x,int y) { int summ=0; while(bel[x]!=bel[y]) { if(dep[bel[x]]>dep[bel[y]]) { summ+=ask(1,1,n,dfn[bel[x]],dfn[x]).second; x=fa[bel[x]]; } else { summ+=ask(1,1,n,dfn[bel[y]],dfn[y]).second; y=fa[bel[y]]; } } if(siz[x]>siz[y]) summ+=ask(1,1,n,dfn[x],dfn[y]).second; else summ+=ask(1,1,n,dfn[y],dfn[x]).second; return summ; } void change(int x,int l,int r,int d,int v) { if(l==r) { c[x]={v,v}; return ; } int mid=(l+r)/2; if(d<=mid) change(x*2,l,mid,d,v); else change(x*2+1,mid+1,r,d,v); c[x].first=max(c[x*2].first,c[x*2+1].first); c[x].second=c[x*2].second+c[x*2+1].second; } int main() { freopen("data.in","r",stdin); freopen("data.out","w",stdout); n=read(); for(int i=2;i<=n;i++) { int x=read(),y=read(); e[x].push_back(y); e[y].push_back(x); } dfs(1); dfs(1,1); for(int i=1;i<=n;i++) change(1,1,n,dfn[i],read()); for(int q=read();q;q--) { string s; cin>>s; if(s=="CHANGE") { int x=read(),t=read(); change(1,1,n,dfn[x],t); } else if(s=="QMAX") { int x=read(),y=read(); cout<<askmax(x,y)<<'\n'; } else { int x=read(),y=read(); cout<<asksum(x,y)<<'\n'; } } }
jzyz2010 [HAOI2015] 树上操作
单点修改,子树修改,路径询问,继续线段树即可
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=100010; int lowbit(int x){return x&(-x);} ll read(){ll x;scanf("%lld",&x);return x;} int n,m,dep[N],siz[N],bel[N],fa[N],dfn[N],tot,a[N]; vector<int>e[N]; ll sum[N*4],lz[N*4]; void dfs(int x) { siz[x]=1; for(auto y:e[x]){ if(y==fa[x]) continue; dep[y]=dep[x]+1; fa[y]=x; dfs(y); siz[x]+=siz[y]; } } void dfs(int x,int chain) { dfn[x]=++tot;bel[x]=chain; int k=0; for(auto y:e[x]) { if(y==fa[x])continue; if(siz[y]>siz[k]) k=y; } if(k)//如果不是叶子 dfs(k,chain); for(auto y:e[x]) { if(y==fa[x]||y==k)continue; dfs(y,y); } } void pushdown(int x,int l,int mid,int r) { if(lz[x]) { lz[x*2]+=lz[x]; lz[x*2+1]+=lz[x]; sum[x*2]+=lz[x]*(mid-l+1); sum[x*2+1]+=lz[x]*(r-mid); lz[x]=0; } } ll ask(int x,int l,int r,int tl,int tr) { if(tl<=l&&r<=tr) return sum[x]; ll t=0; int mid=(l+r)/2; pushdown(x,l,mid,r); if(tl<=mid) t+=ask(x*2,l,mid,tl,tr); if(tr>mid) t+=ask(x*2+1,mid+1,r,tl,tr); return t; } ll asksum(int x,int y) { ll summ=0; while(bel[x]!=bel[y]) { if(dep[bel[x]]>dep[bel[y]]) { summ+=ask(1,1,n,dfn[bel[x]],dfn[x]); x=fa[bel[x]]; } else { summ+=ask(1,1,n,dfn[bel[y]],dfn[y]); y=fa[bel[y]]; } } if(siz[x]>siz[y]) summ+=ask(1,1,n,dfn[x],dfn[y]); else summ+=ask(1,1,n,dfn[y],dfn[x]); return summ; } void add(int x,int l,int r,int tl,int tr,ll v) { if(tl<=l&&r<=tr) { lz[x]+=v; sum[x]+=v*(r-l+1); return ; } int mid=(l+r)/2; pushdown(x,l,mid,r); if(tl<=mid) add(x*2,l,mid,tl,tr,v); if(tr>mid) add(x*2+1,mid+1,r,tl,tr,v); sum[x]=sum[x*2]+sum[x*2+1]; } int main() { freopen("haoi2015.in","r",stdin); freopen("haoi2015.out","w",stdout); n=read();m=read(); for(int i=1;i<=n;i++) a[i]=read(); for(int i=2;i<=n;i++) { int x=read(),y=read(); e[x].push_back(y); e[y].push_back(x); } dfs(1); dfs(1,1); for(int i=1;i<=n;i++) add(1,1,n,dfn[i],dfn[i],a[i]); for(;m;m--) { int t=read(); if(t==1) { int x=read(),v=read(); add(1,1,n,dfn[x],dfn[x],v); } else if(t==2) { int x=read(),v=read(); add(1,1,n,dfn[x],dfn[x]+siz[x]-1,v); } else { int x=read(),y=1; cout<<asksum(x,y)<<'\n'; } } }
luogu3384 重链剖分/树链剖分
路径修改,路径询问,子树修改,子树询问,继续线段树即可
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=100010; int lowbit(int x){return x&(-x);} ll read(){ll x;scanf("%lld",&x);return x;} int n,m,dep[N],siz[N],bel[N],fa[N],dfn[N],tot,a[N]; int mod; vector<int>e[N]; ll c[N*4],lz[N*4]; void dfs(int x) { siz[x]=1; for(auto y:e[x]){ if(y==fa[x]) continue; dep[y]=dep[x]+1; fa[y]=x; dfs(y); siz[x]+=siz[y]; } } void dfs(int x,int chain) { dfn[x]=++tot;bel[x]=chain; int k=0; for(auto y:e[x]) { if(y==fa[x])continue; if(siz[y]>siz[k]) k=y; } if(k)//如果不是叶子 dfs(k,chain); for(auto y:e[x]) { if(y==fa[x]||y==k)continue; dfs(y,y); } } void pushdown(int x,int l,int mid,int r) { if(lz[x]) { c[x*2]=(c[x*2]+lz[x]*(mid-l+1))%mod; c[x*2+1]=(c[x*2+1]+lz[x]*(r-mid))%mod; lz[x*2]=(lz[x*2]+lz[x])%mod; lz[x*2+1]=(lz[x*2+1]+lz[x])%mod; lz[x]=0; } } ll ask(int x,int l,int r,int tl,int tr) { if(tl<=l&&r<=tr) return c[x]; ll t=0; int mid=(l+r)/2; pushdown(x,l,mid,r); if(tl<=mid) t=(t+ask(x*2,l,mid,tl,tr))%mod; if(tr>mid) t=(t+ask(x*2+1,mid+1,r,tl,tr))%mod; return t; } void add(int x,int l,int r,int tl,int tr,ll v) { if(tl<=l&&r<=tr) { c[x]=(c[x]+v*(r-l+1))%mod; lz[x]=(lz[x]+v)%mod; return ; } int mid=(l+r)/2; pushdown(x,l,mid,r); if(tl<=mid) add(x*2,l,mid,tl,tr,v); if(tr>mid) add(x*2+1,mid+1,r,tl,tr,v); c[x]=(c[x*2]+c[x*2+1])%mod; } ll lca_sum(int x,int y) { ll summ=0; while(bel[x]!=bel[y]) { if(dep[bel[x]]>dep[bel[y]]) { summ=(summ+ask(1,1,n,dfn[bel[x]],dfn[x]))%mod; x=fa[bel[x]]; } else { summ=(summ+ask(1,1,n,dfn[bel[y]],dfn[y]))%mod; y=fa[bel[y]]; } } if(siz[x]>siz[y]) summ=(summ+ask(1,1,n,dfn[x],dfn[y]))%mod; else summ=(summ+ask(1,1,n,dfn[y],dfn[x]))%mod; return summ; } void lca_add(int x,int y,int v) { while(bel[x]!=bel[y]) { if(dep[bel[x]]>dep[bel[y]]) { add(1,1,n,dfn[bel[x]],dfn[x],v); x=fa[bel[x]]; } else { add(1,1,n,dfn[bel[y]],dfn[y],v); y=fa[bel[y]]; } } if(siz[x]>siz[y]) add(1,1,n,dfn[x],dfn[y],v); else add(1,1,n,dfn[y],dfn[x],v); } int main() { n=read();m=read(); int rt=read();mod=read(); for(int i=1;i<=n;i++) a[i]=read(); for(int i=1;i<n;i++) { int x=read(),y=read(); e[x].push_back(y); e[y].push_back(x); } dep[rt]=1; dfs(rt); dfs(rt,rt); for(int i=1;i<=n;i++) add(1,1,n,dfn[i],dfn[i],a[i]); for(int i=1;i<=m;i++) { int t=read(); if(t==1) { int x=read(),y=read(),v=read(); lca_add(x,y,v); } else if(t==2) { int x=read(),y=read(); cout<<lca_sum(x,y)<<'\n'; } else if(t==3) { int x=read(),v=read(); add(1,1,n,dfn[x],dfn[x]+siz[x]-1,v); } else { int x=read(); cout<<ask(1,1,n,dfn[x],dfn[x]+siz[x]-1)<<'\n'; } } }
jzyz586 [树链剖分]模板题
如果不看换根,本题和上一题一样:路径修改,子树修改,路径询问,子树询问
现在有了换根操作,这对于路径修改和路径询问毫无影响
而对于子树操作,如果rt=x,则对整棵树进行修改/询问即可
如果rt并非x的子树内的点,则等价于没换根,依然对[dfn[x],dfn[x]+siz[x]-1]进行修改/询问即可
否则比较困难,需要先找到x为根的子树里,rt属于x的哪个儿子son。然后等价于整棵树操作后son子树内部反向操作一下。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=100010; int lowbit(int x){return x&(-x);} ll read(){ll x;scanf("%lld",&x);return x;} int n,dep[N],siz[N],bel[N],fa[N],dfn[N],tot,a[N]; int son[N]; vector<int>e[N]; ll c[N*4],lz[N*4]; void dfs(int x) { siz[x]=1; for(auto y:e[x]){ if(y==fa[x]) continue; dep[y]=dep[x]+1; fa[y]=x; dfs(y); siz[x]+=siz[y]; } } void dfs(int x,int chain) { dfn[x]=++tot;bel[x]=chain; int k=0; for(auto y:e[x]) { if(y==fa[x])continue; if(siz[y]>siz[k]) k=y; } if(k)//如果不是叶子 { son[x]=k; dfs(k,chain); } for(auto y:e[x]) { if(y==fa[x]||y==k)continue; dfs(y,y); } } void pushdown(int x,int l,int mid,int r) { if(lz[x]) { c[x*2]=c[x*2]+lz[x]*(mid-l+1); c[x*2+1]=c[x*2+1]+lz[x]*(r-mid); lz[x*2]=lz[x*2]+lz[x]; lz[x*2+1]=lz[x*2+1]+lz[x]; lz[x]=0; } } ll ask(int x,int l,int r,int tl,int tr) { if(tl<=l&&r<=tr) return c[x]; ll t=0; int mid=(l+r)/2; pushdown(x,l,mid,r); if(tl<=mid) t=t+ask(x*2,l,mid,tl,tr); if(tr>mid) t=t+ask(x*2+1,mid+1,r,tl,tr); return t; } void add(int x,int l,int r,int tl,int tr,ll v) { if(tl<=l&&r<=tr) { c[x]=c[x]+v*(r-l+1); lz[x]=lz[x]+v; return ; } int mid=(l+r)/2; pushdown(x,l,mid,r); if(tl<=mid) add(x*2,l,mid,tl,tr,v); if(tr>mid) add(x*2+1,mid+1,r,tl,tr,v); c[x]=c[x*2]+c[x*2+1]; } ll lca_sum(int x,int y) { ll summ=0; while(bel[x]!=bel[y]) { if(dep[bel[x]]>dep[bel[y]]) { summ=summ+ask(1,1,n,dfn[bel[x]],dfn[x]); x=fa[bel[x]]; } else { summ=summ+ask(1,1,n,dfn[bel[y]],dfn[y]); y=fa[bel[y]]; } } if(siz[x]>siz[y]) summ=summ+ask(1,1,n,dfn[x],dfn[y]); else summ=summ+ask(1,1,n,dfn[y],dfn[x]); return summ; } void lca_add(int x,int y,int v) { while(bel[x]!=bel[y]) { if(dep[bel[x]]>dep[bel[y]]) { add(1,1,n,dfn[bel[x]],dfn[x],v); x=fa[bel[x]]; } else { add(1,1,n,dfn[bel[y]],dfn[y],v); y=fa[bel[y]]; } } if(siz[x]>siz[y]) add(1,1,n,dfn[x],dfn[y],v); else add(1,1,n,dfn[y],dfn[x],v); } int getson(int u,int v) { while(bel[u]!=bel[v]) { if(dep[bel[u]]<dep[bel[v]]) swap(u,v); if(fa[bel[u]]==v) return bel[u]; u=fa[bel[u]]; } if(dep[u]<dep[v]) return son[u]; return son[v]; } int main() { freopen("tree.in","r",stdin); freopen("tree.out","w",stdout); n=read(); int rt=1; for(int i=1;i<=n;i++) a[i]=read(); for(int i=2;i<=n;i++) e[read()].push_back(i); dep[rt]=1; dfs(rt); dfs(rt,rt); for(int i=1;i<=n;i++) add(1,1,n,dfn[i],dfn[i],a[i]); for(int m=read();m;m--) { int t=read(); if(t==1) rt=read(); else if(t==2) { int x=read(),y=read(),v=read(); lca_add(x,y,v); } else if(t==3) { int x=read(),v=read(); if(x==rt)//根=x add(1,1,n,1,n,v); else if(dfn[x]<=dfn[rt]&&dfn[rt]<=dfn[x]+siz[x]-1)//rt是x的儿子,等价于全体加,son子树减 { add(1,1,n,1,n,v); int son=getson(x,rt); add(1,1,n,dfn[son],dfn[son]+siz[son]-1,-v); } else//rt不在x子树内,等价于x子树加 add(1,1,n,dfn[x],dfn[x]+siz[x]-1,v); } else if(t==4) { int x=read(),y=read(); cout<<lca_sum(x,y)<<'\n'; } else { int x=read(); if(x==rt) cout<<c[1]<<'\n'; else if(dfn[x]<=dfn[rt]&&dfn[rt]<=dfn[x]+siz[x]-1)//rt是x的儿子,等价于全体减去son子树 { int son=getson(x,rt); cout<<c[1]-ask(1,1,n,dfn[son],dfn[son]+siz[son]-1)<<'\n'; } else { cout<<ask(1,1,n,dfn[x],dfn[x]+siz[x]-1)<<'\n'; } } } }
「SDOI2014」旅行
单点颜色修改,单点权值修改,路径求和,路径求最大值,对每个颜色开一个动态开点线段树即可解决。从颜色c1转为c2时,不需要真的删掉,只需要在c1线段树里令权值变为0即可。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=100010; int lowbit(int x){return x&(-x);} ll read(){ll x;scanf("%lld",&x);return x;} int n,dep[N],siz[N],bel[N],fa[N],dfn[N],tot,c[N],w[N],rt[N],lc[N*20],rc[N*20]; int son[N]; vector<int>e[N]; ll maxx[N*20],summ[N*20]; void dfs(int x) { siz[x]=1; for(auto y:e[x]){ if(y==fa[x]) continue; dep[y]=dep[x]+1; fa[y]=x; dfs(y); siz[x]+=siz[y]; } } void dfs(int x,int chain) { dfn[x]=++tot;bel[x]=chain; int k=0; for(auto y:e[x]) { if(y==fa[x])continue; if(siz[y]>siz[k]) k=y; } if(k)//如果不是叶子 { son[x]=k; dfs(k,chain); } for(auto y:e[x]) { if(y==fa[x]||y==k)continue; dfs(y,y); } } void add(int &x,int l,int r,int d,ll v) { if(!x) x=++tot; if(l==r) { maxx[x]=summ[x]=v; return ; } int mid=(l+r)/2; if(d<=mid) add(lc[x],l,mid,d,v); else add(rc[x],mid+1,r,d,v); maxx[x]=max(maxx[lc[x]],maxx[rc[x]]); summ[x]=summ[lc[x]]+summ[rc[x]]; } ll ask_sum(int x,int l,int r,int tl,int tr) { if(!x) return 0; if(tl<=l&&r<=tr) return summ[x]; int mid=(l+r)/2; ll t=0; if(tl<=mid) t+=ask_sum(lc[x],l,mid,tl,tr); if(tr>mid) t+=ask_sum(rc[x],mid+1,r,tl,tr); return t; } ll ask_max(int x,int l,int r,int tl,int tr) { if(!x) return 0; if(tl<=l&&r<=tr) return maxx[x]; int mid=(l+r)/2; ll t=0; if(tl<=mid) t=max(t,ask_max(lc[x],l,mid,tl,tr)); if(tr>mid) t=max(t,ask_max(rc[x],mid+1,r,tl,tr)); return t; } ll lca_sum(int x,int y) { // cout<<x<<'?'<<y<<endl; ll summ=0,C=c[x]; while(bel[x]!=bel[y]) { if(dep[bel[x]]>dep[bel[y]]) { summ=summ+ask_sum(rt[C],1,n,dfn[bel[x]],dfn[x]); x=fa[bel[x]]; } else { summ=summ+ask_sum(rt[C],1,n,dfn[bel[y]],dfn[y]); y=fa[bel[y]]; } } if(siz[x]>siz[y]) summ=summ+ask_sum(rt[C],1,n,dfn[x],dfn[y]); else summ=summ+ask_sum(rt[C],1,n,dfn[y],dfn[x]); return summ; } ll lca_max(int x,int y) { ll t=1,C=c[x]; while(bel[x]!=bel[y]) { if(dep[bel[x]]>dep[bel[y]]) { t=max(t,ask_max(rt[C],1,n,dfn[bel[x]],dfn[x])); x=fa[bel[x]]; } else { t=max(t,ask_max(rt[C],1,n,dfn[bel[y]],dfn[y])); y=fa[bel[y]]; } } if(siz[x]>siz[y]) t=max(t,ask_max(rt[C],1,n,dfn[x],dfn[y])); else t=max(t,ask_max(rt[C],1,n,dfn[y],dfn[x])); return t; } int main() { // freopen("tree.in","r",stdin); // freopen("tree.out","w",stdout); n=read(); int m=read(); for(int i=1;i<=n;i++) { w[i]=read(); c[i]=read(); } for(int i=1;i<n;i++) { int x=read(),y=read(); e[x].push_back(y); e[y].push_back(x); } dep[1]=1; dfs(1); dfs(1,1); tot=0; for(int i=1;i<=n;i++) add(rt[c[i]],1,n,dfn[i],w[i]); for(;m;m--) { char s[5]; scanf("%s",s); if(s[1]=='C') { int x=read(),C=read(); if(C==c[x]) continue; add(rt[c[x]],1,n,dfn[x],0); c[x]=C; add(rt[c[x]],1,n,dfn[x],w[x]); } else if(s[1]=='W') { int x=read(); w[x]=read(); add(rt[c[x]],1,n,dfn[x],w[x]); } else if(s[1]=='S') { int x=read(),y=read(); cout<<lca_sum(x,y)<<'\n'; } else { int x=read(),y=read(); cout<<lca_max(x,y)<<'\n'; } } }
[SDOI2011]染色
路径染色,询问路径上颜色数量
也是经典线段树,考虑维护区间左端点颜色lc和右端点颜色rc,则合并的时候新sum为b.sum+c.sum-(b.rc==c.lc)
算答案时,考虑x跳到bel[x]能收益那么多颜色段,但是如果fa[bel[x]]颜色等于bel[x],颜色段减一即可
最后在同一个重链上时,直接累加,不需要减了
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=200010; int lowbit(int x){return x&(-x);} ll read(){ll x;scanf("%lld",&x);return x;} int n,dep[N],siz[N],bel[N],fa[N],dfn[N],tot,nfd[N],c[N],lz[N*4]; vector<int>e[N]; struct node { int lc,rc,sum; }o[4*N]; node merge(node b,node c) { return {b.lc,c.rc,b.sum+c.sum-(b.rc==c.lc)}; } void pushdown(int x) { if(lz[x]) { lz[x*2]=lz[x*2+1]=lz[x]; o[x*2]={lz[x],lz[x],1}; o[x*2+1]={lz[x],lz[x],1}; lz[x]=0; } } node ask(int x,int l,int r,int tl,int tr)//区间询问颜色数量 { if(tl<=l&&r<=tr) return o[x]; pushdown(x); int mid=(l+r)/2; if(tr<=mid) return ask(x*2,l,mid,tl,tr); if(tl>mid) return ask(x*2+1,mid+1,r,tl,tr); return merge(ask(x*2,l,mid,tl,tr),ask(x*2+1,mid+1,r,tl,tr)); } void cover(int x,int l,int r,int tl,int tr,int c) { if(tl<=l&&r<=tr) { lz[x]=c; o[x]={c,c,1}; return ; } pushdown(x); int mid=(l+r)/2; if(tl<=mid) cover(x*2,l,mid,tl,tr,c); if(tr>mid) cover(x*2+1,mid+1,r,tl,tr,c); o[x]=merge(o[x*2],o[x*2+1]); } void dfs(int x) { siz[x]=1; for(auto y:e[x]){ if(y==fa[x]) continue; dep[y]=dep[x]+1; fa[y]=x; dfs(y); siz[x]+=siz[y]; } } void dfs(int x,int chain) { bel[x]=chain; dfn[x]=++tot; nfd[tot]=x; int k=0; for(auto y:e[x]) { if(fa[x]==y) continue; if(siz[y]>siz[k]) k=y; } if(k)//如果不是叶子 dfs(k,chain); for(auto y:e[x]) { if(y==k||y==fa[x]) continue; dfs(y,y); } } int lca_sum(int x,int y) { int t=0; while(bel[x]!=bel[y]) { if(dep[bel[x]]>dep[bel[y]]) { t+=ask(1,1,n,dfn[bel[x]],dfn[x]).sum; if(ask(1,1,n,dfn[bel[x]],dfn[bel[x]]).lc==ask(1,1,n,dfn[fa[bel[x]]],dfn[fa[bel[x]]]).lc) t--; x=fa[bel[x]]; } else { t+=ask(1,1,n,dfn[bel[y]],dfn[y]).sum; if(ask(1,1,n,dfn[bel[y]],dfn[bel[y]]).lc==ask(1,1,n,dfn[fa[bel[y]]],dfn[fa[bel[y]]]).lc) t--; y=fa[bel[y]]; } } if(siz[x]>siz[y]) t+=ask(1,1,n,dfn[x],dfn[y]).sum; else t+=ask(1,1,n,dfn[y],dfn[x]).sum; return t; } void lca_cover(int x,int y,int c) { while(bel[x]!=bel[y]) { if(dep[bel[x]]>dep[bel[y]]) { cover(1,1,n,dfn[bel[x]],dfn[x],c); x=fa[bel[x]]; } else { cover(1,1,n,dfn[bel[y]],dfn[y],c); y=fa[bel[y]]; } } if(siz[x]>siz[y]) cover(1,1,n,dfn[x],dfn[y],c); else cover(1,1,n,dfn[y],dfn[x],c); } void build(int x,int l,int r) { if(l==r) { o[x]={c[nfd[l]],c[nfd[l]],1}; return ; } int mid=(l+r)/2; build(x*2,l,mid); build(x*2+1,mid+1,r); o[x]=merge(o[x*2],o[x*2+1]); } int main() { n=read();int m=read(); for(int i=1;i<=n;i++) c[i]=read(); for(int i=1;i<n;i++) { int x=read(),y=read(); e[x].push_back(y); e[y].push_back(x); } dep[1]=1; dfs(1); dfs(1,1); build(1,1,n); for(;m;m--) { char c; cin>>c; if(c=='C'){ int x=read(),y=read(); lca_cover(x,y,read()); } else cout<<lca_sum(read(),read())<<'\n'; } }
jzyz2371 [NOI2015] 软件包管理器
不妨设安装过了的颜色是1,没安装过的颜色是0
install x需要输出x到1这个链的0的数量,然后x到1这个链染色为1
uninstall x需要输出以x为根的子树的1的数量,然后将这个子树染色为0
可以发现线段树懒标记维护区间0和1的数量即可,询问只需要询问0的数量,这样install在链上跳的时候直接+=,而uninstall时只需要用siz[x]-子树内1的数量即可。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=100010; int lowbit(int x){return x&(-x);} ll read(){ll x;scanf("%lld",&x);return x;} int n,dep[N],siz[N],bel[N],fa[N],dfn[N],tot,nfd[N],lz[N*4]; vector<int>e[N]; int sum0[N*4],sum1[N*4]; void pushdown(int x,int l,int mid,int r) { if(lz[x]==-1) return ; lz[x*2]=lz[x*2+1]=lz[x]; sum1[x*2]=mid-l+1; sum1[x*2+1]=r-mid; sum0[x*2]=0; sum0[x*2+1]=0; if(lz[x]==0) swap(sum1[x*2],sum0[x*2]),swap(sum1[x*2+1],sum0[x*2+1]); lz[x]=-1; } int ask(int x,int l,int r,int tl,int tr)//区间询问0的数量 { if(tl<=l&&r<=tr) return sum0[x]; int mid=(l+r)/2; pushdown(x,l,mid,r); if(tr<=mid) return ask(x*2,l,mid,tl,tr); if(tl>mid) return ask(x*2+1,mid+1,r,tl,tr); return ask(x*2,l,mid,tl,tr)+ask(x*2+1,mid+1,r,tl,tr); } void cover(int x,int l,int r,int tl,int tr,int c) { if(tl<=l&&r<=tr) { lz[x]=c; sum1[x]=r-l+1; sum0[x]=0; if(c==0) swap(sum1[x],sum0[x]); return ; } int mid=(l+r)/2; pushdown(x,l,mid,r); if(tl<=mid) cover(x*2,l,mid,tl,tr,c); if(tr>mid) cover(x*2+1,mid+1,r,tl,tr,c); sum1[x]=sum1[x*2]+sum1[x*2+1]; sum0[x]=sum0[x*2]+sum0[x*2+1]; } void dfs(int x) { siz[x]=1; for(auto y:e[x]){ dep[y]=dep[x]+1; dfs(y); siz[x]+=siz[y]; } } void dfs(int x,int chain) { bel[x]=chain; dfn[x]=++tot; nfd[tot]=x; int k=0; for(auto y:e[x]) { if(siz[y]>siz[k]) k=y; } if(k)//如果不是叶子 dfs(k,chain); for(auto y:e[x]) { if(y==k) continue; dfs(y,y); } } int lca(int x,int y) { int t=0; while(bel[x]!=bel[y]) { if(dep[bel[x]]>dep[bel[y]]) { t+=ask(1,1,n,dfn[bel[x]],dfn[x]); cover(1,1,n,dfn[bel[x]],dfn[x],1); x=fa[bel[x]]; } else { t+=ask(1,1,n,dfn[bel[y]],dfn[y]); cover(1,1,n,dfn[bel[y]],dfn[y],1); y=fa[bel[y]]; } } if(siz[x]>siz[y]){ t+=ask(1,1,n,dfn[x],dfn[y]); cover(1,1,n,dfn[x],dfn[y],1); } else{ t+=ask(1,1,n,dfn[y],dfn[x]); cover(1,1,n,dfn[y],dfn[x],1); } return t; } int main() { freopen("manager.in","r",stdin); freopen("manager.out","w",stdout); n=read(); for(int i=2;i<=n;i++) { fa[i]=read()+1; e[fa[i]].push_back(i); } dep[1]=1; dfs(1); dfs(1,1); memset(lz,-1,sizeof(lz)); lz[1]=0;//区间赋值为0 sum0[1]=n; for(int m=read();m;m--) { string s; cin>>s; if(s=="install"){ cout<<lca(1,read()+1)<<'\n';//链的0的数量顺带cover 1 } else{ int x=read()+1; //询问1的数量即可 cout<<siz[x]-ask(1,1,n,dfn[x],dfn[x]+siz[x]-1)<<'\n'; cover(1,1,n,dfn[x],dfn[x]+siz[x]-1,0); } } }

浙公网安备 33010602011771号