复习:自己以前瞎写的树dfs序

半年前的内容,2月12日前来考古。

这里枚举了树的DFS序来解决树上问题的多个板子,自己最好多看看。

↓改↓ ↓求↓
————————>>>这个就算了
简单, BIT
重点!
简单, 线段树
重重点!!!
简单, BIT+差分
重重点!!!
重重点!!!
重重点!!!

给定一颗树, 和每个节点的权值

1.对某个节点X权值加上一个数W, 查询某个子树X里所有点权的和

改点求树

这个不说什么,果断就BIT维护区间和,即改点求段。

#include<bits/stdc++.h>
using namespace std; const int MAXN=100000; inline int Read(){ int x=0,f=0;char ch; while(!isdigit(ch=getchar()))if(ch=='-') f=1; while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^'0'),ch=getchar(); return f?-x:x; } vector<int> tree[MAXN]; int seq[MAXN],st[MAXN],ed[MAXN]; int n,x,y,cnt,q,m; inline int lowbit(int x){return x&-x;} inline void Add(int x,int k){while(x<=n) seq[x]+=k,x+=lowbit(x);} inline int Sum(int x){int ans=0;while(x>0) ans+=seq[x],x-=lowbit(x);return ans;} void DFS(int fa,int rt){ st[rt]=++cnt; for(register int i=0;i<tree[rt].size();++i) if(tree[rt][i]!=fa) DFS(rt,tree[rt][i]); ed[rt]=cnt; } int main(){ n=Read(),m=Read();//root------1 for(register int i=1;i<n;++i) x=Read(),y=Read(),tree[x].push_back(y),tree[y].push_back(x); DFS(0,1); for(register int i=1;i<=n;++i) y=Read(),Add(st[i],y); for(register int i=1;i<=m;++i){ q=Read();x=Read(); if(q==1) y=Read(),Add(st[x],y); else printf("%d\n",Sum(ed[x])-Sum(st[x]-1)); } return 0; } 

2.对节点X到Y的最短路上所有点权都加一个数W, 查询某个点的权值

改链求点

思路:操作等价于是:

1.对X到根节点路径上所有点权加W
2.对Y到根节点路径上所有点权加W
3.对LCA(x, y)到根节点路径上所有点权值减W
4.对LCA(x,y)的父节点 parent(LCA(x,y))parent(LCA(x, y))parent(LCA(x,y))到根节点路径上所有权值减W

这里的操作我 用一个树状数组seq[i]seq[i]seq[i]表示在DFS序里的节点i到根节点的整体被操作的值,这样操作1就是Add(st[x],w)Add(st[x],w)Add(st[x],w) 2,3,4同理。查询自己看代码想一想,就是子树中总共的操作值。(感谢@hankeke帮助juruo理解)

#include<bits/stdc++.h>
using namespace std; const int MAXN=100000; const int Log=19; inline int Read(){ int x=0,f=0;char ch; while(!isdigit(ch=getchar()))if(ch=='-') f=1; while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^'0'),ch=getchar(); return f?-x:x; } vector<int> tree[MAXN]; int f[MAXN][Log+2]; int seq[MAXN],st[MAXN],ed[MAXN],A[MAXN],depth[MAXN],s[MAXN]; int n,x,y,cnt,q,m,w; inline int lowbit(int x){return x&-x;} inline void Add(int x,int k){if(!x) return; while(x<=n) seq[x]+=k,x+=lowbit(x);} inline int Sum(int x){int ans=0;while(x>0) ans+=seq[x],x-=lowbit(x);return ans;} void Power(int k,int tot){ while(k<=n+1){ int kk=k;k<<=1; for(register int i=kk;i<=k-1;++i) s[i]=tot; ++tot; } } void DFS(int fa,int rt){ st[rt]=++cnt;f[rt][0]=fa;depth[rt]=depth[fa]+1; for(register int j=1;(1<<j)<=depth[rt];++j) f[rt][j]=f[f[rt][j-1]][j-1]; for(register int i=0;i<tree[rt].size();++i) if(tree[rt][i]!=fa) DFS(rt,tree[rt][i]); ed[rt]=cnt; } inline int LCA(int x,int y){ if(depth[x]<depth[y]) x^=y^=x^=y; for(register int k=/*s[depth[x]-depth[y]]*/19;k>=0;--k) if(depth[f[x][k]]>=depth[y]) x=f[x][k]; if(x==y) return x; for(register int k=/*s[depth[x]-1]*/19;k>=0;--k) if(f[x][k]!=f[y][k]) x=f[x][k],y=f[y][k]; return f[x][0]; } int main(){//freopen("tmp.txt","r",stdin); n=Read(),m=Read();//root------1 for(register int i=1;i<n;++i) x=Read(),y=Read(),tree[x].push_back(y),tree[y].push_back(x); DFS(0,1);//Power(1,0); for(register int i=1;i<=n;++i) A[i]=Read(); for(register int i=1;i<=m;++i){ q=Read(); if(q==1){ x=Read(),y=Read(),w=Read(); int lca=LCA(x,y); Add(st[x],w);Add(st[y],w);Add(st[lca],-w);Add(st[f[lca][0]],-w); } else x=Read(),printf("%d\n",A[x]+Sum(ed[x])-Sum(st[x]-1)); } return 0; } 

3.对节点X到Y的最短路上所有点权都加一个数W, 查询某个点子树的权值之和

改链求树

思路极乱

#include<bits/stdc++.h>
using namespace std; const int MAXN=100000; const int Log=19; inline int Read(){ int x=0,f=0;char ch; while(!isdigit(ch=getchar()))if(ch=='-') f=1; while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^'0'),ch=getchar(); return f?-x:x; } vector<int> tree[MAXN]; int f[MAXN][Log+2]; int seq1[MAXN],seq2[MAXN],depth[MAXN],sum[MAXN]; int st[MAXN],ed[MAXN]; int n,x,y,cnt,q,m,w; inline int lowbit(int x){return x&-x;} inline void Add1(int x,int k){if(!x) return; while(x<=n) seq1[x]+=k,x+=lowbit(x);} inline int Sum1(int x){int ans=0;while(x>0) ans+=seq1[x],x-=lowbit(x);return ans;} inline void Add2(int x,int k){if(!x) return; while(x<=n) seq2[x]+=k,x+=lowbit(x);} inline int Sum2(int x){int ans=0;while(x>0) ans+=seq2[x],x-=lowbit(x);return ans;} /* void Power(int k,int tot){ while(k<=n+1){ int kk=k;k<<=1; for(register int i=kk;i<=k-1;++i) s[i]=tot; ++tot; } } */ void DFS(int fa,int rt){ st[rt]=++cnt;f[rt][0]=fa;depth[rt]=depth[fa]+1; for(register int j=1;(1<<j)<=depth[rt];++j) f[rt][j]=f[f[rt][j-1]][j-1]; for(register int i=0;i<tree[rt].size();++i) if(tree[rt][i]!=fa) DFS(rt,tree[rt][i]); ed[rt]=cnt; } inline int LCA(int x,int y){ if(depth[x]<depth[y]) x^=y^=x^=y; for(register int k=/*s[depth[x]-depth[y]]*/19;k>=0;--k) if(depth[f[x][k]]>=depth[y]) x=f[x][k]; if(x==y) return x; for(register int k=/*s[depth[x]-1]*/19;k>=0;--k) if(f[x][k]!=f[y][k]) x=f[x][k],y=f[y][k]; return f[x][0]; } int main(){//freopen("tmp.txt","r",stdin); n=Read(),m=Read();//root------1 for(register int i=1;i<n;++i) x=Read(),y=Read(),tree[x].push_back(y),tree[y].push_back(x); DFS(0,1);//Power(1,0); for(register int i=1;i<=n;++i) sum[st[i]]=Read(); for(register int i=1;i<=n;++i) sum[i]+=sum[i-1]; for(register int i=1;i<=m;++i){ q=Read(); if(q==1){ x=Read(),y=Read(),w=Read(); int lca=LCA(x,y); Add1(st[x],w);Add1(st[y],w);Add1(st[lca],-w);Add1(st[f[lca][0]],-w); Add2(st[x],w*(depth[x]+1));Add2(st[y],w*(depth[y]+1));Add2(st[lca],-w*(depth[lca]+1));Add2(st[f[lca][0]],-w*(depth[f[lca][0]]+1)); } else x=Read(),printf("%d\n",sum[ed[x]]-sum[st[x]-1]+Sum2(ed[x])-Sum2(st[x]-1)-(Sum1(ed[x])-Sum1(st[x]-1))*depth[x]); } return 0; } 

4.对某个点X权值加上一个数W, 查询X到Y路径上所有点权之和

改点求链

用一个树状数组seq[i]seq[i]seq[i]表示在DFS序里的节点i到根节点的链权值和被加一个w,x到y的路径亦可拆成4条,改一个点以后就用差分思想把其子树点区间加w(树状数组区间修改seq,求点),查询时对x和y到根的链预处理好的权值和加上总操作值。可对比T2。

扫描二维码关注公众号,回复: 5163618 查看本文章
#include<bits/stdc++.h>
using namespace std; const int MAXN=100000; const int Log=19; inline int Read(){ int x=0,f=0;char ch; while(!isdigit(ch=getchar()))if(ch=='-') f=1; while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^'0'),ch=getchar(); return f?-x:x; } vector<int> tree[MAXN]; int f[MAXN][Log+2]; int len[MAXN],st[MAXN],ed[MAXN],A[MAXN],depth[MAXN],sum[MAXN]; int n,x,y,cnt,q,m,w; inline int lowbit(int x){return x&-x;} inline void Add(int x,int k){if(!x) return; while(x<=n) len[x]+=k,x+=lowbit(x);} inline int Sum(int x){int ans=0;while(x>0) ans+=len[x],x-=lowbit(x);return ans;} /* void Power(int k,int tot){ while(k<=n+1){ int kk=k;k<<=1; for(register int i=kk;i<=k-1;++i) s[i]=tot; ++tot; } } */ void DFS(int fa,int rt){ st[rt]=++cnt;f[rt][0]=fa;depth[rt]=depth[fa]+1;sum[rt]=sum[fa]+A[rt]; for(register int j=1;(1<<j)<=depth[rt];++j) f[rt][j]=f[f[rt][j-1]][j-1]; for(register int i=0;i<tree[rt].size();++i) if(tree[rt][i]!=fa) DFS(rt,tree[rt][i]); ed[rt]=cnt; } inline int LCA(int x,int y){ if(depth[x]<depth[y]) x^=y^=x^=y; for(register int k=/*s[depth[x]-depth[y]]*/19;k>=0;--k) if(depth[f[x][k]]>=depth[y]) x=f[x][k]; if(x==y) return x; for(register int k=/*s[depth[x]-1]*/19;k>=0;--k) if(f[x][k]!=f[y][k]) x=f[x][k],y=f[y][k]; return f[x][0]; } int main(){//freopen("tmp.txt","r",stdin); n=Read(),m=Read();//root------1 for(register int i=1;i<n;++i) x=Read(),y=Read(),tree[x].push_back(y),tree[y].push_back(x); for(register int i=1;i<=n;++i) A[i]=Read(); DFS(0,1);//Power(1,0); for(register int i=1;i<=m;++i){ q=Read(); if(q==1) x=Read(),w=Read(),Add(st[x],w),Add(ed[x]+1,-w); else{ x=Read(),y=Read(); int lca=LCA(x,y);printf("%d\n",sum[x]+sum[y]-sum[lca]-sum[f[lca][0]]+Sum(st[x])+Sum(st[y])-Sum(st[lca])-Sum(st[f[lca][0]])); } } return 0; } 

5.对子树X里所有节点加上一个值W, 查询某个点的值

改树求点

BIT+差分,水过。

#include<bits/stdc++.h>
using namespace std; const int MAXN=100000; inline int Read(){ int x=0,f=0;char ch; while(!isdigit(ch=getchar()))if(ch=='-') f=1; while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^'0'),ch=getchar(); return f?-x:x; } vector<int> tree[MAXN]; int seq[MAXN],st[MAXN],ed[MAXN],A[MAXN]; int n,x,y,cnt,q,m; inline int lowbit(int x){return x&-x;} inline void Add(int x,int k){while(x<=n) seq[x]+=k,x+=lowbit(x);} inline int Sum(int x){int ans=0;while(x>0) ans+=seq[x],x-=lowbit(x);return ans;} void DFS(int fa,int rt){ st[rt]=++cnt; for(register int i=0;i<tree[rt].size();++i) if(tree[rt][i]!=fa) DFS(rt,tree[rt][i]); ed[rt]=cnt; } int main(){ n=Read(),m=Read();//root------1 for(register int i=1;i<n;++i) x=Read(),y=Read(),tree[x].push_back(y),tree[y].push_back(x); DFS(0,1); for(register int i=1;i<=n;++i) A[i]=Read(); for(register int i=1;i<=m;++i){ q=Read();x=Read(); if(q==1) y=Read(),Add(st[x],y),Add(ed[x]+1,-y); else printf("%d\n",A[x]+Sum(st[x])); } return 0; } 

6.对子树X里所有节点加上一个值W, 查询某个子树的值

改树求树

线段树,区间修改区间查询水过。其实BIT也是可以的,看这里树状数组区间修改区间查询1或者这里树状数组区间修改区间查询2

#include<bits/stdc++.h>
#define l i<<1 #define r i<<1|1 #define mid ((L+R)>>1) using namespace std; const int MAXN=100000; inline int Read(){ int x=0,f=0;char ch; while(!isdigit(ch=getchar()))if(ch=='-') f=1; while(isdigit(ch)) x=(x<<1)+(x<<3)+(ch^'0'),ch=getchar(); return f?-x:x; } vector<int> tree[MAXN]; int st[MAXN],ed[MAXN],A[MAXN]; int sum[MAXN<<2],add[MAXN<<2]; int n,x,y,cnt,q,m,ql,qr,w; void build(int i,int L,int R){ if(L==R)sum[i]=A[L]; else build(l,L,mid),build(r,mid+1,R),sum[i]=sum[l]+sum[r]; } inline void pushdown(int i,int L,int R){ if(add[i]) sum[l]+=(mid-L+1)*add[i],sum[r]+=(R-mid)*add[i],add[l]+=add[i],add[r]+=add[i],add[i]=0; } inline void Add(int i,int L,int R){ if(ql<=L&&qr>=R){sum[i]+=w*(R-L+1),add[i]+=w;return;} pushdown(i,L,R); if(ql<=mid) Add(l,L,mid);if(qr>mid) Add(r,mid+1,R); sum[i]=sum[l]+sum[r]; } inline int Query(int i,int L,int R){ if(ql<=L&&qr>=R) return sum[i]; pushdown(i,L,R);int ans=0; if(ql<=mid) ans+=Query(l,L,mid);if(qr>mid) ans+=Query(r,mid+1,R); return ans; } void DFS(int fa,int rt){ st[rt]=++cnt; for(register int i=0;i<tree[rt].size();++i) if(tree[rt][i]!=fa) DFS(rt,tree[rt][i]); ed[rt]=cnt; } int main(){ n=Read(),m=Read();//root------1 for(register int i=1;i<n;++i) x=Read(),y=Read(),tree[x].push_back(y),tree[y].push_back(x); DFS(0,1); for(register int i=1;i<=n;++i) A[st[i]]=Read(); build(1,1,n); for(register int i=1;i<=m;++i){ q=Read(),x=Read();ql=st[x],qr=ed[x]; if(q==1) w=Read(),Add(1,1,n); else printf("%d\n",Query(1,1,n)); } return 0; } 

7.对于子树X内的所有点都加上一个值W,查询X到Y之间的路径上所有点权和。

改树求链

写不动了。。

8.补充

改点求点

改点求链?

改链求链?

猜你喜欢

转载自www.cnblogs.com/saigyouji-yuyuko/p/10367461.html