Luogu P3128 [USACO15DEC]最大流Max Flow
题目概述:
给定一棵有N个节点的树,接下来有K个操作Order(U,V),使得U和V间的道路上的所有节点(包括U和V)权值都加1,求操作后树上节点权值的最大值。
数据规模:
2≤N≤50,000,1≤K≤100,000)
思路:
LCA+前缀和,先DFS遍历树,再用倍增实现LCA,对于每次操作Order(U,V),将两点的最近公共祖先记为LCA,那么设置一个前缀和修改数组。处理如下:change[U]++,change[V]++,change[LCA]–,change[fa[LCA][0]]–;再DFS递归遍历树,更新前缀和数组,然后求个最大值即可。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
using namespace std;
int i,j,m,n,s,fa[50001][25],hd[50001];
int deep[50001],b[50001],rd[50001];
int y[400001],nxt[400001],temp,sum[50001],ans[50001],aans;
int r()
{
int aans=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
aans*=10;
aans+=ch-'0';
ch=getchar();
}
return aans*f;
}
void add(int xx,int yy)
{
nxt[++temp]=hd[xx];
y[temp]=yy;
hd[xx]=temp;
}
int lca(int xx,int yy)
{
int i,j;
if(deep[xx]<deep[yy])swap(xx,yy);
for(i=0; (1<<i)<=deep[xx]; i++);
i--;
for(j=i; j>=0; j--)
if(deep[xx]-(1<<j)>=deep[yy])
xx=fa[xx][j];
if(xx==yy)return xx;
for(j=i; j>=0; j--)
{
if(fa[xx][j]!=fa[yy][j])
{
xx=fa[xx][j];
yy=fa[yy][j];
}
}
return fa[xx][0];
}
void dfs(int x)
{
int p,son;
p=hd[x];
while(p)
{
son=y[p];
if(!b[son])
{
b[son]=1;
rd[x]++;
fa[son][0]=x;
deep[son]=deep[x]+1;
int ii=0,po=x;
while(fa[po][ii]!=0)
{
fa[son][ii+1]=fa[po][ii];
po=fa[po][ii];
ii++;
}
dfs(son);
}
p=nxt[p];
}
}
void dfs_sum(int x)
{
int p,son;
p=hd[x];
while(p)
{
son=y[p];
if(deep[son]>deep[x])
{
dfs_sum(son);
sum[x]+=sum[son];
}
p=nxt[p];
}
aans=max(sum[x],aans);
}
int main()
{
freopen("in.txt","r",stdin);
n=r(),m=r(),s=1;
int xx,yy;
for(i=1; i<n; i++)
{
xx=r(),yy=r();
add(xx,yy);
add(yy,xx);
}
deep[s]=1;
b[s]=1;
dfs(s);
for(int j=0; j<m; j++)
{
scanf("%d%d",&xx,&yy);
int l=lca(xx,yy);
sum[xx]++;
sum[yy]++;
sum[fa[l][0]]--;
sum[l]--;
}
dfs_sum(1);
cout<<aans;
}