P3128 [USACO15DEC]最大流Max Flow
对,这是一道最大流的题目qwq
树上跑最大流,没错
也就是跑最小割
你看名字里都有最大流,为什么不能跑最大流qwq..............................
编不下去了
跑树上差分。
就像数组上可以进行差分一样,树上也可以进行差分。适用于树上两点之间的路径操作,和极少数的查询
适用于维护路径长可以进行结合律的操作
比如说我们要在树上进行两点之间的路径上的节点值加一个数,最后输出所有节点值之和。
我们可以进行树链剖分,可是使用树链剖分有些大材小用了,而且常数巨大
我们就可以使用差分
比如说这么一棵树
我们要使得8到11的路径上的点加3
我们就可以设一个数组,\(t[i]\)表示这棵树的差分数组,\(st[i][j]\)表示树上的倍增数组
然后我们令\(t[8]+=3,t[11]+=3,t[lca_{(8,11)}]-=3,t[st[lca_{(8,11)}][0]]-=3\)
然后我们将差分数组使用以下程序进行整理整理,然后进行查询。
void DFS(int now,int fa)
{
int pas=0;
for(int i=head[now];i;i=line[i].nxt)
if(line[i].p!=fa)
{
DFS(line[i].p,now);
t[now]+=t[line[i].p];
}
}
将所有点上的值递归加起来,就成了下图的情况。成功实现了O(1)修改,O(N)查询
而对于维护边权呢?我们可以将边权下放至所连点中深度最深的点上去
然后将差分时的\(t[st[lca_{(a,b)}][0]]-=add,t[lca_{(a,b)}]-=add\)变为\(t[lca_{(a,b)}]-add*2\)就可以了,树链剖分也可以将边权下放至点
此题\(code\)
#include<cstdio>
#include<algorithm>
#include<iostream>
using std::swap;
using std::max;
const int maxn=50100;
struct node
{
int p;
int nxt;
};
node line[maxn<<1];
int head[maxn],tail;
void add(int a,int b)
{
line[++tail].p=b;
line[tail].nxt=head[a];
head[a]=tail;
}
int st[maxn][20];
int log[maxn];
int dep[maxn];
int ans,t[maxn];
void dfs(int now,int fa)
{
st[now][0]=fa;
dep[now]=dep[fa]+1;
for(int i=1;i<=log[dep[now]];i++)
st[now][i]=st[st[now][i-1]][i-1];
for(int i=head[now];i;i=line[i].nxt)
if(line[i].p!=fa)
dfs(line[i].p,now);
}
int lca(int a,int b)
{
if(dep[a]<dep[b]) swap(a,b);
for(int i=log[dep[a]];i>=0;i--)
if(dep[st[a][i]]>=dep[b])
a=st[a][i];
if(a==b) return a;
for(int i=log[dep[a]];i>=0;i--)
if(st[a][i]!=st[b][i])
a=st[a][i],b=st[b][i];
return st[a][0];
}
void DFS(int now,int fa)
{
int pas=0;
for(int i=head[now];i;i=line[i].nxt)
if(line[i].p!=fa)
{
DFS(line[i].p,now);
t[now]+=t[line[i].p];
}
ans=max(ans,t[now]);
}
int main()
{
int n,k;
scanf("%d%d",&n,&k);
for(int i=2;i<=n;i++) log[i]=log[i>>1]+1;
int a,b;
for(int i=1;i<n;i++)
{
scanf("%d%d",&a,&b);
add(a,b);add(b,a);
}
dfs(1,0);
for(int i=1;i<=k;i++)
{
scanf("%d%d",&a,&b);
t[a]++;t[b]++;
int LCA=lca(a,b);
t[LCA]--;
t[st[LCA][0]]--;
}
DFS(1,0);
printf("%d",ans);
}