CF903G Yet Another Maxflow Problem
题目传送门
分析:
考虑把最大流转换成最小割来处理这个问题
考虑这个图的特性:
如果说我们要割去\(A_i\)到\(A_{i+1}\)的边,那么对于\(j>i\),我们就没必要割去\(A_j\)到\(A_{j+1}\)的边了
\(B\)同理,这样\(A\)和\(B\)中我们只需要至多各自割掉一条边
因为有不割\(A\)或\(B\),而多割一些中间的边代价更小的情况,由于处理起来比较麻烦,我们可以优化一下
\(A_0\)到\(A_1\),\(B_n\)到\(B_{n+1}\)连代价为0的边,求\(A_0\)到\(B_{n+1}\)的最小割
这样\(A\)和\(B\)中我们就可以各自割掉恰好一条边找到最小代价
假设割\(A_u\)到\(A_{u+1}\),\(B_v\)到\(B_{v+1}\),那么答案为
\(a_u+b_v+sum_{i\leq u,j>v}c_{i,j}\)
由于修改的只有\(a_u\),对于每个\(v\),后面的一部分的最小代价不收影响
按顺序枚举\(u\),加入必须割掉的边,用线段树维护取哪个\(v\)代价最小,即维护全局最小值
修改直接单点修改就好了
复杂度\(O(nlogn)\)
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<queue>
#include<set>
#include<map>
#include<vector>
#define maxn 200005
#define pii pair<int,int>
using namespace std;
inline int getint()
{
int num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
return num*flag;
}
int n,m,q;
long long A[maxn],B[maxn],C[maxn];
vector<pii >G[maxn];
long long mn[maxn<<2],lz[maxn<<2];
inline void build(int i,int l,int r)
{
lz[i]=0;
if(l==r){mn[i]=B[l];return;}
int mid=(l+r)>>1;
build(i<<1,l,mid),build(i<<1|1,mid+1,r);
mn[i]=min(mn[i<<1],mn[i<<1|1]);
}
inline void pushdown(int i)
{if(lz[i]){mn[i<<1]+=lz[i],mn[i<<1|1]+=lz[i];lz[i<<1]+=lz[i],lz[i<<1|1]+=lz[i];lz[i]=0;}}
inline void update(int i,int l,int r,int ql,int qr,int w)
{
if(ql>r||qr<l)return;
if(ql<=l&&r<=qr){mn[i]+=w,lz[i]+=w;return;}
int mid=(l+r)>>1;
pushdown(i);
update(i<<1,l,mid,ql,qr,w),update(i<<1|1,mid+1,r,ql,qr,w);
mn[i]=min(mn[i<<1],mn[i<<1|1]);
}
int main()
{
n=getint(),m=getint(),q=getint();
for(int i=2;i<=n;i++)A[i-1]=getint(),B[i]=getint();
while(m--)
{
int u=getint(),v=getint(),w=getint();
G[u].push_back(make_pair(v,w));
}
build(1,1,n);
for(int i=1;i<=n;i++)
{
for(int j=0;j<G[i].size();j++)update(1,1,n,1,G[i][j].first,G[i][j].second);
C[i]=mn[1];
}
for(int i=1;i<=n;i++)B[i]=A[i]+C[i];
build(1,1,n);
printf("%lld\n",mn[1]);
while(q--)
{
int p=getint(),x=getint();
update(1,1,n,p,p,x-A[p]),A[p]=x;
printf("%lld\n",mn[1]);
}
}