Maxflow
作者:互联网
题目背景
给你一棵大小为 \(n\) 的树,每个节点上都有一个点权,初值为 0。
一共有 \(m\) 次操作,每次操作给定一个点对 $(s_i,t_i)#,表示对 \(s_i−>t_i\) 这条路径上的每个节点点权+1。
所有操作结束后,询问所有点的最大点权。
输入格式
第一行包括两个正整数 \(n,m\)。
此后 \(n−1\) 行,每行两个正整数,描述着这颗树上的 \(n−1\) 条边。
再此后的 \(m\) 行,每行两个正整数 \(s_i,t_i\),描述一个操作的给定点对。
输出格式
输出一行一个正整数,表示操作后所有点的最大点权。
样例
input
5 10
3 4
1 5
4 2
5 4
5 4
5 4
3 5
4 3
4 3
1 3
3 5
5 4
1 5
3 4
output
9
数据范围
时间限制: 1s
空间限制: 256MB
对于 100% 的数据, \(n,m≤100,000\)。
数据富含梯度,可以各种尝试。
树上差分的模板。
首先既然是差分,那么就以子树和为这个点的权值。首先两个点都增加1,到他们的LCA时差分值减1,LCA的父亲差分值也要减1.
用倍增\(O(nlogn)\),用Tarjan \(O(n)\)
#include<cstdio>
#include<iostream>
using namespace std;
const int N=1e5+5;
int n,m,lg[N],dep[N],fa[N][30],hd[N],c[N],s,t,ans,k;
struct edge{
int v,nxt;
}e[N<<1];
void add_edge(int z,int x,int y)
{
e[z]=(edge){y,hd[x]};
hd[x]=z;
}
void dfs(int x,int y)
{
fa[x][0]=y,dep[x]=dep[y]+1;
for(int i=1;i<=lg[dep[x]];i++)
fa[x][i]=fa[fa[x][i-1]][i-1];
for(int i=hd[x];i;i=e[i].nxt)
if(e[i].v!=y)
dfs(e[i].v,x);
}
int lca(int x,int y)
{
while(dep[x]>dep[y])
x=fa[x][lg[dep[x]-dep[y]]];
while(dep[x]<dep[y])
y=fa[y][lg[dep[y]-dep[x]]];
if(x==y)
return x;
for(int i=lg[dep[x]];i>=0;i--)
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int sou(int x)
{
int s=0;
for(int i=hd[x];i;i=e[i].nxt)
if(e[i].v!=fa[x][0])
s+=sou(e[i].v);
s+=c[x];
ans=max(ans,s);
return s;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)
{
scanf("%d%d",&s,&t);
add_edge(i<<1,s,t);
add_edge(i<<1|1,t,s);
}
for(int i=2;i<=n;i++)
lg[i]=lg[i>>1]+1;
dfs(1,0);
for(int i=1;i<=m;i++)
{
scanf("%d%d",&s,&t),k=lca(s,t);
c[s]++,c[t]++,c[k]--,c[fa[k][0]]--;
}
sou(1);
printf("%d",ans);
return 0;
}
标签:正整数,fa,int,点权,dep,ans,Maxflow 来源: https://www.cnblogs.com/mekoszc/p/16209358.html