树上差分备忘
树上差分备忘
点差分
++cnt[u];
++cnt[v];
--cnt[lca(u,v)];
--cnt[fa[lca(u,v)]];
树上主席树就是这个原理来的
边差分
先把边塞给较深的那个点上
++cnt[u];
++cnt[v];
cnt[lca(u,v)]-=2;
统计
一遍\(dfs\),在回溯时加起来即可
void calc(int u, int fa){
for(int i=head[u];i!=0;i=nxt[i]){
int v=vv[i];
if(v==fa) continue;
calc(v,u);
ans[u]+=ans[v];
}
}
板子
#include <cstdio>
#include <algorithm>
#define MAXN 300003
using namespace std;
int n,a[MAXN];
int head[MAXN],nxt[MAXN*2],vv[MAXN*2],tot;
inline void add_edge(int u, int v){
vv[++tot]=v;
nxt[tot]=head[u];
head[u]=tot;
}
int dep[MAXN],f[MAXN][20];
void dfs(int u, int fa){
dep[u]=dep[fa]+1;
f[u][0]=fa;
for(int i=1;(1<<i)<=dep[u];++i)
f[u][i]=f[f[u][i-1]][i-1];
for(int i=head[u];i!=0;i=nxt[i]){
int v=vv[i];
if(v==fa) continue;
dfs(v,u);
}
}
int get_k(int x){
int ans=0;
int t=1;
while(t<x){
t*=2;
ans++;
}
return ans;
}
inline int lca(int a, int b){
if(dep[a]<dep[b]) swap(a,b);
if(dep[a]!=dep[b]){
for(int i=get_k(dep[a]);i>=0;--i)
if(dep[a]-(1<<i)>=dep[b])
a=f[a][i];
}
if(a==b) return a;
for(int i=get_k(dep[b]);i>=0;i--)
if(f[a][i]!=f[b][i])
a=f[a][i],b=f[b][i];
return f[a][0];
}
int ans[MAXN];
void calc(int u, int fa){
for(int i=head[u];i!=0;i=nxt[i]){
int v=vv[i];
if(v==fa) continue;
calc(v,u);
ans[u]+=ans[v];
}
}
int main()
{
scanf("%d", &n);
for(int i=1;i<=n;++i) scanf("%d", &a[i]);
for(int i=2;i<=n;++i){
int x,y;scanf("%d %d", &x, &y);
add_edge(x,y);
add_edge(y,x);
}
dfs(1,0);
for(int i=1;i<=n-1;++i){
int s=a[i],t=a[i+1];
ans[s]++;
ans[t]++;
int tmp=lca(s,t);
ans[tmp]--;
ans[f[tmp][0]]--;
}
calc(1,0);
for(int i=1;i<=n;++i)
if(i==a[1]) printf("%d\n", ans[i]);
else printf("%d\n", ans[i]-1);
return 0;
}