SPOJ - OPTM Optimal Marks(進位制拆分+最小割)

Frozen_Guardian發表於2020-10-08

題目連結:點選檢視

題目大意:給出一個由 n 個點和 m 條邊組成的圖,有 k 個點初始時就有權值 w[ i ],現在問如何給剩下的節點賦值,使得整張圖的總權值和最小,每條邊的權值為:w( u , v ) = w[ u ] xor w[ v ]

題目分析:因為異或運算屬於位運算,所以對於每一位來說其貢獻都是相互獨立的,可以分開之後分別計算

這樣問題就轉換成了,對於那些未賦值的位置來說,選擇 0 或 1 將其賦值,因為每個位置的取值只有兩種選擇,又是一種最優性問題,不難想到最小割

建圖思路也比較簡單:

  1. st -> 已經確定答案了,且當前位置為 1 的點,流量為 inf
  2. 原圖,流量為 1
  3. 已經確定了答案,且當前位置為 0 的點 -> ed,流量為 inf

這樣求完最小割後,與 st 在一個聯通塊中的點顯然賦值為 1 是更優的,同理與 ed 在一個聯通塊中的點需要賦值為 0

程式碼:
 

//#pragma GCC optimize(2)
//#pragma GCC optimize("Ofast","inline","-ffast-math")
//#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
#include<bitset>
#include<list>
#include<unordered_map> 
using namespace std;

typedef long long LL;

typedef unsigned long long ull;

const int inf=0x3f3f3f3f;

const int N=510;

pair<int,int>e[3100];
 
struct Edge
{
	int to,w,next;
}edge[N*N];//邊數
 
int head[N],cnt,n,m;

unsigned int ans[N];

bool vis[N],book[N];
 
void addedge(int u,int v,int w)
{
	edge[cnt].to=v;
	edge[cnt].w=w;
	edge[cnt].next=head[u];
	head[u]=cnt++;
	edge[cnt].to=u;
	edge[cnt].w=0;//反向邊邊權設定為0
	edge[cnt].next=head[v];
	head[v]=cnt++;
}
 
int d[N],now[N];//深度 當前弧優化
 
bool bfs(int s,int t)//尋找增廣路
{
	memset(d,0,sizeof(d));
	queue<int>q;
	q.push(s);
	now[s]=head[s];
	d[s]=1;
	while(!q.empty())
	{
		int u=q.front();
		q.pop();
		for(int i=head[u];i!=-1;i=edge[i].next)
		{
			int v=edge[i].to;
			int w=edge[i].w;
			if(d[v])
				continue;
			if(!w)
				continue;
			d[v]=d[u]+1;
			now[v]=head[v];
			q.push(v);
			if(v==t)
				return true;
		}
	}
	return false;
}
 
int dinic(int x,int t,int flow)//更新答案
{
	if(x==t)
		return flow;
	int rest=flow,i;
	for(i=now[x];i!=-1&&rest;i=edge[i].next)
	{
		int v=edge[i].to;
		int w=edge[i].w;
		if(w&&d[v]==d[x]+1)
		{
			int k=dinic(v,t,min(rest,w));
			if(!k)
				d[v]=0;
			edge[i].w-=k;
			edge[i^1].w+=k;
			rest-=k;
		}
	}
	now[x]=i;
	return flow-rest;
}
 
void init()
{
    memset(now,0,sizeof(now));
	memset(head,-1,sizeof(head));
	memset(book,false,sizeof(book));
	cnt=0;
}
 
int solve(int st,int ed)
{
	int ans=0,flow;
	while(bfs(st,ed))
		while(flow=dinic(st,ed,inf))
			ans+=flow;
	return ans;
}

void dfs(int u)
{
	book[u]=true;
	for(int i=head[u];i!=-1;i=edge[i].next)
	{
		int v=edge[i].to;
		if(book[v])
			continue;
		if(edge[i].w)
			dfs(v);
	}
}

void solve(int bit)
{
	init();
	int st=N-1,ed=st-1;
	for(int i=1;i<=n;i++)
	{
		if(!vis[i])
			continue;
		if((ans[i]>>bit)&1)
			addedge(st,i,inf);
		else
			addedge(i,ed,inf);
	}
	for(int i=1;i<=m;i++)
	{
		int u,v;
		tie(u,v)=e[i];
		addedge(u,v,1);
		addedge(v,u,1);
	}
	solve(st,ed);
	dfs(st);
	for(int i=1;i<=n;i++)
		if(book[i])
			ans[i]|=(1u<<bit);
}

int main()
{
#ifndef ONLINE_JUDGE
//  freopen("data.in.txt","r",stdin);
//  freopen("data.ans.txt","w",stdout);
#endif
//  ios::sync_with_stdio(false);
	int w;
	cin>>w;
	while(w--)
	{
		memset(ans,0,sizeof(ans));
		memset(vis,false,sizeof(vis));
		scanf("%d%d",&n,&m);
		for(int i=1;i<=m;i++)
			scanf("%d%d",&e[i].first,&e[i].second);
		int k;
		scanf("%d",&k);
		while(k--)
		{
			int x;
			scanf("%d",&x);
			scanf("%u",&ans[x]);
			vis[x]=true;
		}
		for(int i=0;i<=31;i++)
			solve(i);
		for(int i=1;i<=n;i++)
			printf("%u\n",ans[i]);
	}








   return 0;
}

 

相關文章