P3258 [JLOI2014]松鼠的新家
此题树剖可过
然而可以树上差分为什么要树剖..
对于一条路径(A,B),只要把 val [ A ] ++ , val [ B ] ++ , val [ LCA(A,B) ] -- , val [ fa[LCA(A,B)] ] --
那么求每个点的经过次数就求一下子树 val 的和就好了
但是要注意,每一条路径的终点是下一条路径的起点,那些点会被多算一次
所以求完子树和以后要再把那些点 val--
代码很简单
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> using namespace std; const int N=6e5+7; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } int fir[N],from[N<<1],to[N<<1],cnt; inline void add(int &a,int &b) { from[++cnt]=fir[a]; fir[a]=cnt; to[cnt]=b; } int n; int dep[N],f[N][27];//LCA的数组 void dfs1(int x,int fa)//dfs1预处理f和dep { f[x][0]=fa; dep[x]=dep[fa]+1; for(int i=1;(1<<i)<=dep[x];i++) f[x][i]=f[f[x][i-1]][i-1]; for(int i=fir[x];i;i=from[i]) { int &v=to[i]; if(v==fa) continue; dfs1(v,x); } } inline int LCA(int x,int y)//求LCA { if(dep[x]<dep[y]) swap(x,y); for(int i=20;i>=0;i--) if(dep[f[x][i]]>=dep[y]) x=f[x][i]; if(x==y) return x; for(int i=20;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0]; } int a[N],sum[N]; void dfs2(int x)//dfs2求子树和 { for(int i=fir[x];i;i=from[i]) { int &v=to[i]; if(v==f[x][0]) continue; dfs2(v); sum[x]+=sum[v]; } } int main() { int b,c; n=read(); for(int i=1;i<=n;i++) a[i]=read(); for(int i=1;i<n;i++) b=read(),c=read(),add(b,c),add(c,b); dfs1(1,1); int t; for(int i=1;i<n;i++) { sum[a[i]]++; sum[a[i+1]]++; t=LCA(a[i],a[i+1]); sum[t]--; if(t!=1) sum[f[t][0]]--;//注意细节 } dfs2(1); for(int i=2;i<=n;i++) sum[a[i]]--;//减去重复算的 for(int i=1;i<=n;i++) printf("%d\n",sum[i]); return 0; }