题解 P3128 【[USACO15DEC]最大流Max Flow】

XG_Zepto

2018-04-12 11:49:58

Solution

## 思路 实际上就是求被每个点被不同的路径覆盖了几次。只要把每条路径经过的点的答案加1即可。树剖和树状数组或线段树可以轻松搞定。 但是我们发现,这道题可以用LCA和树上查分更加优美而快速地解决。对于一条路径,将两个端点的值加1,它们的LCA和LCA的父亲分别减1,最后DFS累加一下就可以得到每个点的答案,取最大值即可。 ## 代码 不同的模块写得非常清楚了,变量和函数解释如下: - $l$数组和$head$数组:前向星存边。 - $s$数组:记录每个点的答案。 - $d$数组:每个点的深度。 - $p[i][j]$:第j个点向上走i^2步到达的点。 - $Pre$_$Work$函数:预处理出d和p。 - $Solve$函数:求LCA,单点修改。 - $Sum$函数:累加。 ``` #include <bits/stdc++.h> #define maxn 50001 using namespace std; struct Edge{ int to,next; Edge(int a=0,int b=0){ to=a,next=b; } }l[maxn*4]; int head[maxn],s[maxn],n,k,t1,t2; int d[maxn],p[30][maxn],cnt,ans; void Add(int x,int y){ l[++cnt]=Edge(y,head[x]); head[x]=cnt; } void Pre_Work(int u,int f){ d[u]=d[f]+1; p[0][u]=f; for (int i=1;(1<<i)<=d[u];i++) p[i][u]=p[i-1][p[i-1][u]]; for (int i=head[u];i;i=l[i].next){ int v=l[i].to; if (v!=f) Pre_Work(v,u); } } void Solve(int a,int b){ int lca;s[a]++,s[b]++; if (d[a]<d[b]) swap(a,b); for (int i=28;i>=0;i--) if (d[p[i][a]]>=d[b]) a=p[i][a]; if (a==b) lca=a; else { for (int i=28;i>=0;i--) if (p[i][a]!=p[i][b]) a=p[i][a],b=p[i][b]; lca=p[0][a]; } s[lca]--; s[p[0][lca]]--; } void Sum(int u,int f){ for (int i=head[u];i;i=l[i].next){ int v=l[i].to; if (v==f) continue; Sum(v,u); s[u]+=s[v]; } ans=max(s[u],ans); } int main(){ ios::sync_with_stdio(false); cin>>n>>k; for (int i=1;i<n;i++){ cin>>t1>>t2; Add(t1,t2); Add(t2,t1); } Pre_Work(1,0); for (int i=1;i<=k;i++){ cin>>t1>>t2; Solve(t1,t2); } Sum(1,0); cout<<ans<<endl; } ```