bzoj3631[JLOI2014]松鼠的新家
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3631
树上差分。
因为从根到当前点的差分有种种不方便(需要改孩子们的值;需要处理路径上拐出去的其他边等),所以从叶子到根方向地差分!
注意一下起点不+1,终点+1。
#include<iostream> #include<cstdio> #include<cstring> using namespace std; const int N=3e5+5; int n,t[N],head[N],xnt,g[N],fa[N][20],dep[N],ans[N]; struct Edge{ int next,to; Edge(int n=0,int t=0):next(n),to(t) {} }edge[N<<1]; void add(int x,int y) { edge[++xnt]=Edge(head[x],y);head[x]=xnt; edge[++xnt]=Edge(head[y],x);head[y]=xnt; } void dfs(int cr,int f,int d) { fa[cr][0]=f;dep[cr]=d; for(int i=1;fa[fa[cr][i-1]][i-1];i++)fa[cr][i]=fa[fa[cr][i-1]][i-1]; for(int i=head[cr];i;i=edge[i].next) if(edge[i].to!=f)dfs(edge[i].to,cr,d+1); } int lg(int a){int cnt;while(a)a>>=1,cnt++;return cnt;} void solve(int a,int b) { g[fa[a][0]]++;g[b]++; // printf("g[%d]=%d g[%d]=%d\n",a,g[a],b,g[b]); if(dep[a]<dep[b])swap(a,b);int d=dep[a]-dep[b],j=0; while(d) { if(d&1)a=fa[a][j];d>>=1;j++; } if(a==b){g[a]--;g[fa[a][0]]--;return;} for(int j=lg(dep[a]);j>=0;j--) if(fa[a][j]!=fa[b][j])a=fa[a][j],b=fa[b][j]; if(a!=b)a=fa[a][0],b=fa[b][0]; g[a]--;g[fa[a][0]]--; // printf("g[%d]=%d g[%d]=%d\n",a,g[a],fa[a][0],g[fa[a][0]]);printf("lca=%d\n",a); } int dfs2(int cr,int f) { int tmp=g[cr]; for(int i=head[cr],v;i;i=edge[i].next) if((v=edge[i].to)!=f) tmp+=dfs2(v,cr); ans[cr]=tmp;return tmp; } int main() { scanf("%d",&n);int x,y; for(int i=1;i<=n;i++)scanf("%d",&t[i]); for(int i=1;i<n;i++) scanf("%d%d",&x,&y),add(x,y); dfs(1,0,1); for(int i=2;i<=n;i++){ solve(t[i-1],t[i]); // for(int i=1;i<=n;i++)printf("%d ",g[i]);printf("\n"); } dfs2(1,0);ans[t[1]]++;ans[t[n]]--; for(int i=1;i<=n;i++)printf("%d\n",ans[i]); return 0; }