1. 程式人生 > >P3258 [JLOI2014]松鼠的新家

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; }