CF903G Yet Another Maxflow Problem
题目链接:戳我
最小割+线段树模拟网络流
(自己手动画了一个图,有点丑还请见谅)
首先声明一些数组:a[i]表示左边图编号为i的线段的长度,b[i]表示右边图编号为i的线段的长度,sum[i]表示选取左边编号为i的线段的最小代价。
下面我们来看这个题怎么做——
比较神仙。既然题目中都说了"yet another maxflow problem",那肯定不是用最大流来写的。我们考虑转化,转化成最小割。(你问我为什么?我我我。。。我也不太清楚,就当做一个套路吧,而且网络流二分图中的最大流不也很有一部分题目是转化成最小割来写的嘛qwqwq)
转化了之后我们就知道答案肯定是\(dis_{(A_i,A_i+1)}+dis_{(B_i,B_i+1)}+\sum dis_{(p,q)},p\le i,q\ge j\)
我们可以考虑从小到大遍历一遍A,(设现在遍历到的点为u)然后枚举它的出边所指向的v。对于(u,v)这条边,显然它的贡献只产生于\(i\le u,j\ge v\)的边里面。又因为左边遍历的时候已经保证顺序了,所以我们只需要在维护\(b\)值的区间里加上它的值作为贡献即可。
然后我们重新构建一次线段树。。。查询全局最小值。每次修改就是简单的单点修改。
然后。。就没有然后了。。。。就做完了。
代码如下:
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#define MAXN 200010
using namespace std;
int n,m,q,edge_number;
int head[MAXN];
long long a[MAXN],b[MAXN],sum[MAXN];
struct Node{int l,r;long long minn,tag;}t[MAXN<<2];
struct Edge{int nxt,to;long long dis;}edge[MAXN<<1];
inline void add(int from,int to,long long dis)
{
edge[++edge_number].nxt=head[from];
edge[edge_number].to=to;
edge[edge_number].dis=dis;
head[from]=edge_number;
}
inline int ls(int x){return x<<1;}
inline int rs(int x){return x<<1|1;}
inline void push_up(int x){t[x].minn=min(t[ls(x)].minn,t[rs(x)].minn);}
inline void solve(int x,long long k)
{
t[x].tag+=k;
t[x].minn+=k;
}
inline void push_down(int x)
{
if(t[x].tag)
{
solve(ls(x),t[x].tag);
solve(rs(x),t[x].tag);
t[x].tag=0;
}
}
inline void build(int x,int l,int r,int op)
{
t[x].l=l,t[x].r=r;
if(l==r)
{
if(op==0) t[x].minn=b[l];
else t[x].minn=sum[l];
return;
}
int mid=(l+r)>>1;
build(ls(x),l,mid,op);
build(rs(x),mid+1,r,op);
push_up(x);
}
inline void build2(int x,int l,int r)
{
t[x].l=l,t[x].r=r;
if(l==r) {t[x].minn=sum[l];return;}
int mid=(l+r)>>1;
build2(ls(x),l,mid);
build2(rs(x),mid+1,r);
push_up(x);
}
inline void update(int x,int ll,int rr,long long k)
{
int l=t[x].l,r=t[x].r;
if(ll<=l&&r<=rr) {solve(x,k);return;}
int mid=(l+r)>>1;
push_down(x);
if(ll<=mid) update(ls(x),ll,rr,k);
if(mid<rr) update(rs(x),ll,rr,k);
push_up(x);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("ce.in","r",stdin);
#endif
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<n;i++) scanf("%I64d%I64d",&a[i],&b[i]);
for(int i=1;i<=m;i++)
{
int u,v;
long long w;
scanf("%d%d%I64d",&u,&v,&w);
add(u,v,w);
}
build(1,0,n-1,0);
for(int i=1;i<=n;i++)
{
for(int j=head[i];j;j=edge[j].nxt)
{
int v=edge[j].to;
update(1,0,v-1,edge[j].dis);
}
sum[i]=a[i]+t[1].minn;
}
//for(int i=1;i<=n;i++) printf("sum[%d]=%I64d\n",i,sum[i]);
memset(t,0,sizeof(t));
build(1,1,n,1);
printf("%I64d\n",t[1].minn);
for(int i=1;i<=q;i++)
{
int u;
long long k;
scanf("%d%I64d",&u,&k);
update(1,u,u,k-a[u]);
a[u]=k;
printf("%I64d\n",t[1].minn);
}
return 0;
}