A11 树上差分



D09 倍增算法 P3379【模板】最近公共祖先(LCA) - 董晓 - 博客园
Luogu P3128 [USACO15DEC] Max Flow P
#include <iostream> #include <cstring> #include <algorithm> using namespace std; const int N = 50005, M=2*N; int h[N], to[M], ne[M], tot; int dep[N],f[N][22]; int n,m,ans,power[N]; void add(int x,int y){ to[++tot]=y,ne[tot]=h[x],h[x]=tot; } void dfs(int u,int fa){ //倍增预处理 dep[u]=dep[fa]+1, f[u][0]=fa; for(int i=1; i<=20; i++) f[u][i]=f[f[u][i-1]][i-1]; for(int i=h[u]; i; i=ne[i]){ int v=to[i]; if(v!=fa) dfs(v,u); } } int lca(int u,int v){ //倍增求lca if(dep[u]<dep[v])swap(u, v); for(int i=20; ~i; i--) if(dep[f[u][i]]>=dep[v]) u=f[u][i]; if(u==v) return v; for(int i=20; ~i; i--) if(f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i]; return f[u][0]; } void dfs2(int u,int fa){ //统计答案 for(int i=h[u]; i; i=ne[i]){ int v=to[i]; if(v==fa) continue; dfs2(v,u); power[u]+=power[v]; } ans=max(ans,power[u]); } int main(){ scanf("%d%d",&n,&m); for(int i=1,x,y;i<n;++i){ scanf("%d%d",&x,&y); add(x,y); add(y,x); } dfs(1,0); for(int i=1,x,y; i<=m; ++i){ scanf("%d%d",&x,&y); int l=lca(x,y); ++power[x];++power[y]; //树上差分 --power[l];--power[f[l][0]]; } dfs2(1,0); printf("%d\n",ans); }
练习:
// LCA+树上差分 O(mlogn) #include<bits/stdc++.h> using namespace std; const int N=100010,M=N<<1; int n,m,ans; int h[N],to[M],ne[M],idx; int dep[N],fa[N][17],d[N]; //d[x]表示节点x到根节点的距离,即该路径上边被覆盖的次数 void add(int u,int v){ to[++idx]=v,ne[idx]=h[u],h[u]=idx; } void bfs(){ //预处理dep,fa数组 dep[1]=1; queue<int> q; q.push(1); while(q.size()){ int u=q.front(); q.pop(); for(int i=h[u]; i; i=ne[i]){ int v=to[i]; if(dep[v]) continue; dep[v]=dep[u]+1; q.push(v); fa[v][0]=u; for(int k=1; k<=16; k++) fa[v][k]=fa[fa[v][k-1]][k-1]; } } } int lca(int u,int v){ if(dep[u]<dep[v]) swap(u,v); for(int k=16; k>=0; k--) if(dep[fa[u][k]]>=dep[v]) u=fa[u][k]; if(u==v) return u; for(int k=16; k>=0; k--) if(fa[u][k]!=fa[v][k]) u=fa[u][k], v=fa[v][k]; return fa[u][0]; } int dfs(int u,int father){ //对子树的差分求和 int sum=d[u]; for(int i=h[u]; i; i=ne[i]){ int j=to[i]; if(j==father) continue; int s=dfs(j,u); if(s==0) ans+=m; //统计方案数 else if(s==1) ans++; sum+=s; } return sum; //u子树的结点权值和,即(u,fa)边覆盖次数 } int main(){ scanf("%d%d",&n,&m); for(int i=0,u,v; i<n-1; i++){ scanf("%d%d",&u,&v); add(u,v),add(v,u); } bfs(); for(int i=0,u,v; i<m; i++){ scanf("%d%d",&u,&v); int p=lca(u,v); d[u]++,d[v]++,d[p]-=2; //树上差分 } dfs(1,0); //差分求和 printf("%d\n",ans); }
Luogu P2680 [NOIP2015 提高组] 运输计划
Luogu P1600 [NOIP2016 提高组] 天天爱跑步
浙公网安备 33010602011771号