acwing2280. 最优标号

太久没做网络流的 思维题了,智力急速下降。。。

这个题很妙

主要分析建图想法

摘自

注意,多条边的交叉情况也是基于这两种的。发现它具有网络流的性质吧?所以考虑它怎么和网络流联系在一起。把初始点权为1的看做S集合,点权为0的看做T集合。我们其实要做的就是处理完S到T的所有边,使代价最小。把代价当做割开一条路径的花费,所求问题即为割最少的边使得S到T不连通。

主要思想就是 :: 划分集合 + 隔绝集合

很显然对于一个点来说,任意有两个点权值不同的
显然就要给这个点赋权来断开

那么对于多叉的同理

对于一段连续的无权值的
可以缩成一起

就这样就解决了

最小割在这里起到的分离集合的作用
瓶颈是想到将点权不同的点划分入相应集合,并且在原图上将其分离
还是比较妙的。。。

#include<bits/stdc++.h>
#define MAXN 7005
typedef long long ll;
using namespace std;

int n,m,h[MAXN],tot,k,s,t;
ll p[MAXN],ans,zz;
int dep[MAXN],vis[MAXN],cur[MAXN],gg;

struct node{
	int from,to,next;
	ll rest;
}e[MAXN << 1] , e2[MAXN << 1];

void add(int x , int y , ll z){
	tot++;
	e[tot].from = x;
	e[tot].to = y;
	e[tot].rest = z;
	e[tot].next = h[x];
	h[x] = tot;
}

void init(){
	tot = (-1);
	memset(p , -1 , sizeof(p));
	memset(h , -1 , sizeof(h));
	cin>>n>>m;
	for(int i = 1 ; i <= m ; i++)cin>>e2[i].from>>e2[i].to;
	cin>>k;ll x,y;
	for(int i = 1 ; i <= k ; i++){
		cin>>x>>y;
		p[x] = y;
	}
}

int bfs(){
	memset(dep , 0x3f , sizeof(dep));
	memset(vis , 0 , sizeof(vis));
	int now;queue<int>q;q.push(s);
	vis[s] = dep[s] = 1;
	while(!q.empty()){
		now = q.front() , q.pop();vis[now] = 0;
		for(int i = h[now] ; i != (-1) ; i = e[i].next){
			if(!e[i].rest || dep[e[i].to] <= dep[now] + 1)continue;
			dep[e[i].to] = dep[now] + 1;
			if(!vis[e[i].to])vis[e[i].to] = 1 , q.push(e[i].to);
		}
	}
	if(dep[t] == 0x3f3f3f3f)return 0;
	return 1;
}

ll dfs(int now , ll low){
	if(now == t){
		ans += low;
		gg = 1;
		return low;
	}
	ll used = low , res;
	for(int i = h[now] ; i != (-1) ; i = e[i].next){
		if(!e[i].rest || dep[e[i].to] != dep[now] + 1)continue;
		if(res = dfs(e[i].to , min(used , e[i].rest))){
			used -= res;
			e[i].rest -= res;
			e[i ^ 1].rest += res;
			if(!used)break;
		}
	}
	return low - used;
}

void dinic(){
	while(bfs()){
		gg = 1;
		while(gg){
			gg = 0;
			dfs(s , 0x3f3f3f3f);
		}
	}
}

void solve(){
	s = zz = ans = 0 , t = n + 1;
	for(ll cishu = 30 ; cishu >= 0 ; cishu--){
		memset(h , -1 , sizeof(h));tot = (-1);
		for(int i = 1 ; i <= n ; i++){
			if(p[i] == (-1))continue;
			if((p[i] >> cishu) & 1)add(i , t , 0x3f3f3f3f) , add(t , i , 0);
			else add(s , i , 0x3f3f3f3f) , add(i , s , 0);
		}
		for(int i = 1 ; i <= m ; i++){
			add(e2[i].from , e2[i].to , 1);
			add(e2[i].to , e2[i].from , 1);
		}
		ans = 0;
		dinic();
		zz += (1 << cishu) * ans;
	}
	cout<<zz<<endl;
}

int main(){
	init();
	solve();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值