充分理清限制与条件+构造二分图+最小割:ARC142E

https://www.luogu.com.cn/problem/AT_arc142_e

考虑 $$

他的充要条件是是什么:

  1. a i , a j ≥ m i n ( b i , b j ) a_i,a_j\ge min(b_i,b_j) ai,ajmin(bi,bj)
  2. 存在 a i ≥ m a x ( b i , b j ) a_i\ge max(b_i,b_j) aimax(bi,bj)

第一个条件直接预处理一下。

第二个条件,同时有非常多限制交叉,我们就非常想网络流建模来搞。(而且数据范围明细提示着我们)但是有个尴尬的问题,哪些点连源点,哪些连汇点。

然后就是此题最巧妙的地方了,可以构造二分图!

我们发现 ( a i , b i ) (a_i,b_i) (ai,bi) 是成对的,这似乎提示着我们什么。考虑对其分类

对于一对限制 ( i , j ) (i,j) (i,j),如果目前只满足条件1,不满足条件2 , a i ≥ b i a_i\ge b_i aibi a j ≥ b j a_j\ge b_j ajbj 必然是一个成立,一个不成立。同时,这里 i , j i,j i,j 独立,所以这就变成了一个二分图了!

最后是建模。我们这里发现是左边和右边至少满足一个,典型的最小割模型。

#include<bits/stdc++.h>

using namespace std;
#define int long long
inline int read(){int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;
ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+
(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
//#define M
//#define mo
#define N 100010


using namespace atcoder;

struct node {
	int u, v;  
};
int n, m, i, j, k, T;
int ans, a[N], b[N], u, v, S; 
vector<node>G; 

signed main()
{
//	freopen("in.txt", "r", stdin);
//	freopen("out.txt", "w", stdout);
//	srand(time(NULL));
//	T=read();
//	while(T--) {
//
//	}	
	mf_graph<int>F(200010); 
	n=read(); S=(n+5)*100+1; T=(n+5)*100+2; 
	for(i=1; i<=n; ++i) a[i]=read(), b[i]=read(); 
	m=read(); 
	for(i=1; i<=m; ++i) {
		u=read(); v=read(); 
		k=min(b[u], b[v]);  
		if(a[u]<k) ans+=k-a[u], a[u]=k; 
		if(a[v]<k) ans+=k-a[v], a[v]=k; 
		G.pb({u, v}); 
	}
//	printf("%lld\n", ans); 
	for(i=1; i<=n; ++i) {
//		printf("%lld %lld\n", a[i], b[i]); 
		if(a[i]>=b[i]) {
//			printf("> %lld\n", i); 
			for(j=a[i]+1; j<=100; ++j) 
				F.add_edge(S, i*100+j, 1); 
			for(j=1; j<100; ++j) 
				F.add_edge(i*100+j, i*100+j+1, 1e9); 
		}
		else {
			for(j=a[i]+1; j<=100; ++j) 
				F.add_edge(i*100+j, T, 1); 
			for(j=1; j<100; ++j) 
			F.add_edge(i*100+j+1, i*100+j, 1e9); 
		}
		
	}
	for(auto t : G) {
		u=t.u; v=t.v; 
		k=max(b[u], b[v]); 
		if(a[u]>=b[u] && a[v]>=b[v]) continue; 
		if(a[u]>=b[v] && a[v]>=b[u]) continue; 
//		if(k==13) continue; 
		if(a[u]>=b[u]) {
//					printf("%d -> %d\n", u*100+k, v*100+k); 

			F.add_edge(u*100+k, v*100+k, 1e9); 
		}
		else {
//					printf("%d -> %d\n", v*100+k, u*100+k); 

			F.add_edge(v*100+k, u*100+k, 1e9); 
		}
	}
	ans=ans+F.flow(S, T); 
	printf("%lld", ans); 
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值