10.18二中校内T3图论+线段树+网络流(??)

22 篇文章 0 订阅
19 篇文章 0 订阅

T3 网络network

题干

**机房中的电脑被排成了两排,每一排都有?台电脑。我们现在对其标号,一 排依次标号为??(? = 1,2,…,?),另一排依次标号为??(? = 1,2,…,?)。
它们之间通过网线进行连接,每一条网线都有一定的传输上限,并且只能进 行单向传输。对于每排中的电脑,??向??+1连边,??向??+1连边。另外有?根网 线,连接??与??。
现在共有?次操作,每次操作会修改某条从??到??+1的网线的容量上限,每 一次你都要求出修改后从?1到??的最大流量。
Tips: 最大流最小割定理:在一个网络流中,能够从源点到达汇点的最大流 量等于如果从网络中移除就能够导致网络流中断的边的集合的最小容量和。 **

在这里插入图片描述

solution

一个点到另一个点的最大流量就对应着最小割。 也就是说必须要在左边割一条边,在右边割一条边 假设左边这条 边靠近1的点是a,右边割的边靠近n的点是b 那么所有连接1-a,b-n的点都要被割掉
也就是说,左边哪一条边作为割时,对应的最小流量是固定的,这样取一个最小值就是最小割。
我们发现这样可以把一种割分为两部分:
1、左边的一条边的权值
2、选这条边作为割,中间及右边的割产生的最小值
所以我们现在要维护的就是左边每一条边作为割时,中间及右边的割产生的最小值。也就是要在右边选一条边作为 割,使得中间的对应边和右边的这条边的和最小。
考虑这个如何维护。建一棵线段树,首先对其中结点赋值,为右边每条边的边权,表示在右边选这一条边为割所产 生的权值,注意因为边有可能直接连到汇点,所以要增加一个权值为0的边。
然后我们从1-n将左边的边逐一进行考虑,每加入一条边,中间就会多一些边连向右边,加入左边一条边时,枚举 当前点连向右边的所有边,我们将线段树中e.to-n这个区间内加上这个边的值 (实际加在1-e.to上) ,表示左边当前边再往下的边作为割 时都要考虑当前加入的这些边。可以看出每次加入左边一条边并更新完线段树后,线段树中的最小值在加上这条边 的权值,就是这条边所对应的最小割。
用这种方法就可以求出,左边每条边作为割时,所对应的最小割的值。
考虑求答案,我们将求出来的这些值维护成一棵线段树,显然其中的最小值就是最小割,那么对于询问怎样维护。
由于我们求的是左边每一条边作为割所时所对应的最小割,所以这时候修改左边边的值也就可以直接修改了,这样 直接修改线段树,然后输出最小值就可以了。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=300100;
const int M=300100;
ll L[N],R[N];
ll read()
{
	char ch=' ';
	while(ch<'0'||ch>'9')
	{
		ch=getchar();
	}
	ll x=0;
	while(ch>='0'&&ch<='9')
	{
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x;
}
struct node
{
	int v,nxt;
	ll w;
}edge[M];
int head[N],cnt=0;
ll sum[N<<2];
ll tag[N<<2];
ll t[N];
ll x,y,k;
void add(int u,int v,ll w)
{
	cnt++;
	edge[cnt].v=v;
	edge[cnt].w=w;
	edge[cnt].nxt=head[u];
	head[u]=cnt;
}
void build(int rt,int l,int r,ll* z)
{
	tag[rt]=0;
	if(l==r)
	{
		sum[rt]=z[l];
		return ;
	}
	int mid=(l+r) >> 1;
	build(rt<<1,l,mid,z);
	build(rt<<1|1,mid+1,r,z);
	sum[rt]=min(sum[rt<<1],sum[rt<<1|1]);
}
void pushdown(int rt,int l,int r)
{
	if(tag[rt])
	{
		ll kk=tag[rt];
		tag[rt]=0;
		int mid=(l+r) >> 1;
		tag[rt<<1]+=kk;
		sum[rt<<1]+=kk;
		tag[rt<<1|1]+=kk;
		sum[rt<<1|1]+=kk;
	}
}
void modify(int rt,int l,int r)
{
	if(l>=x&&r<=y)
	{
		sum[rt]+=k;
		tag[rt]+=k;
		return ;
	}
	pushdown(rt,l,r);
	int mid=(l+r) >> 1;
	if(x<=mid)
	{
		modify(rt<<1,l,mid);
	}
	if(y>mid)
	{
		modify(rt<<1|1,mid+1,r);
	}
	sum[rt]=min(sum[rt<<1],sum[rt<<1|1]);
}
void print(ll q){
	if(q >= 10) print(q / 10);
	putchar(q % 10 + '0');
}
int main()
{
	freopen("network.in","r",stdin);
   freopen("network.out","w",stdout);
	int n=read(),m=read(),q=read();
	int i,j;
	for(i=1;i<=n-1;i++)
	{
		L[i]=read();
	}
	for(i=1;i<=n-1;i++)
	{
		R[i+1]=read();
	}
	for(i=1;i<=m;i++)
	{
		int u=read(),v=read(),w=read();
		add(u,v,w);
	}
	build(1,1,n,R);
	for(i=1;i<=n;i++)
	{
		for(j=head[i];j;j=edge[j].nxt)
		{
			x=1;
			y=edge[j].v;
			k=edge[j].w;
			modify(1,1,n);
		}
		t[i]=sum[1]+L[i];
	}
	build(1,1,n,t);
	printf("%lld\n",sum[1]);
	for(i=1;i<=q;i++)
	{
		x=read();y=x;
		ll val=read();
		k=val-L[x];
		modify(1,1,n);
		L[x]=val;
		print(sum[1]);putchar('\n');
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值