在考了$n$次树上差分之后我终于要来学习树上差分辣.(其实今天也只是心血来潮
看的是这篇博客.
用来干啥的
统计点被经过的次数或者是边被经过的次数.(反正我现在暂时只知道这个$qwq$).
将一般差分中的前缀和转换成一个结点的子树和.
点的差分
差分数组$t[]$.
走过一条路径$(u,v)$.那么就$t[u]++,t[v]++,t[lca(u,v)]--,t[fa[lca(u,v)]]--$.
边的差分
转换成点的,具体来说要转换成边连接的子结点.
走过一条路径$(u,v)$.那么就$t[u]++,t[v]++,t[lca(u,v)]-=2$.
题目
#include<bits/stdc++.h> #define il inline #define Ri register int #define go(i,a,b) for(Ri i=a;i<=b;++i) #define yes(i,a,b) for(Ri i=a;i>=b;--i) #define e(i,u) for(Ri i=b[u];i;i=a[i].nt) #define mem(a,b) memset(a,b,sizeof(a)) #define ll long long #define db double #define inf 2147483647 using namespace std; il int read() { Ri x=0,y=1;char c=getchar(); while(c<‘0‘||c>‘9‘){if(c==‘-‘)y=-1;c=getchar();} while(c>=‘0‘&&c<=‘9‘){x=(x<<1)+(x<<3)+c-‘0‘;c=getchar();} return x*y; } const int N=50010; int n,k,b[N],ct,t[N],dep[N],f[N][21],as; struct nd{int v,nt;}a[N*2]; il void add(Ri u,Ri v){a[++ct]=(nd){v,b[u]};b[u]=ct;} il void build(Ri u,Ri fa) { dep[u]=dep[fa]+1; f[u][0]=fa; go(i,1,20)f[u][i]=f[f[u][i-1]][i-1]; e(i,u) { if(a[i].v==fa)continue; build(a[i].v,u); } } il int lca(Ri u,Ri v) { if(dep[u]<dep[v])swap(u,v); yes(i,20,0)if(dep[f[u][i]]>dep[v])u=f[u][i]; if(dep[u]!=dep[v])u=f[u][0]; if(u==v)return v; yes(i,20,0)if(f[u][i]!=f[v][i])u=f[u][i],v=f[v][i]; return f[u][0]; } il int dfs(Ri u) { Ri ret=0; e(i,u) { if(a[i].v==f[u][0])continue; ret+=dfs(a[i].v); } ret+=t[u];as=max(as,ret); return ret; } int main() { n=read(),k=read(); go(i,1,n-1){Ri u=read(),v=read();add(u,v);add(v,u);} build(1,0); go(i,1,k) { Ri u=read(),v=read(),lc=lca(u,v); t[u]++;t[v]++;t[lc]--;t[f[lc][0]]--; } dfs(1);printf("%d\n",as); return 0; }
原文:https://www.cnblogs.com/forward777/p/11559501.html