[JZOJ6231] 【NOI2019模擬6.25】等你哈蘇德【圖論】【歐拉回路】【網絡流】

Description

數軸上有一些線段,需要將它們染成黑或白色,有些已經染好了顏色,現在求一種染色方案使得對於所有整點,覆蓋它的黑色線段和白色線段數之差的絕對值不超過1

n<=30000n<=30000

Solution

我們把白色看做+1,黑色看做-1,問題變成要求每個位置的值只能是[1,0,1][-1,0,1]

由於是區間加減,我們考慮差分變成兩個點的加和減
因此可以將左端點和右端點+1拉出來離散化,只有這些點是有用的
一個點加,一個點減,我們將兩個點之間連一條有向邊,那麼就意味着一個點出度+1,一個點入度+1

假定要求黑色線段和白色線段數之差要等於0,那麼就是所有的點入度=出度,它存在一條歐拉回路,我們只需要想辦法讓所有點入度=出度即可。

這實際上就是混合圖的歐拉回路問題,我們將未定向的邊隨機定向,新建源點匯點,源點向出度大於入度的點連容量爲(出度-入度)/2,反之連向匯點,原圖的邊容量全爲1,跑一遍最大流,一條增廣路就意味着將路徑上所有邊反向,兩個出入度不等的點調整了一下,且中間的所有點不改變,合法的條件就是新邊全部滿流。

如果我們隨機定向的邊被流過了說明這條邊的方向定反了。
注意已經有方向的邊要計算度數,但不能在網絡中連這條邊(因爲不能修改)

迴歸原問題,我們要求絕對值不超過1,也就是說我們可以補上一些邊使得它存在歐拉回路。

如果直接將度數爲奇數的點拉出來連邊是錯誤的
我們考慮這個東西-1,1,0,它差分的結果是-1,2,-1

2是偶點,它不會被連邊,但它並不能構成歐拉回路。
實際上2應該向兩個-1各連一條邊,而不是-1之間連起來。

於是我們修改補邊辦法,將所有關鍵點拉出來不去重的排序,第2i12i-1個點與第2i2i個點之間連一條隨機定向的邊,這樣在保證了所有點都是偶點的情況下也滿足了上面的情況(顯然不可能連續兩個2)。

跑一遍最大流即可。

Code

#include <bits/stdc++.h>
#define fo(i,a,b) for(int i=a;i<=b;++i)
#define fod(i,a,b) for(int i=a;i>=b;--i)
const int N=30005;
using namespace std;
int n,n1,m,ap[N][3],cs;
struct px
{
	int x,w,p;
	friend bool operator <(px x,px y)
	{
		return x.x<y.x;
	}
}dw[N<<2];
int ans[N],pf[N<<1];
namespace flow
{
	int fs[N<<1],nt[N<<3],dt[N<<3],pr[N<<3],fx[N<<3],m1,in[N<<1],out[N<<1],st,ed,d[N<<2],h[N<<2];
	void link(int x,int y,int z)
	{
		nt[++m1]=fs[x];
		dt[fs[x]=m1]=y;
		pr[m1]=z;
	}
	void addedge(int x,int y,int z)
	{
		link(x,y,z),link(y,x,0);
		out[x]++,in[y]++;
		fx[m1]=m1-1,fx[m1-1]=m1;
	}
	bool bfs()
	{
		memset(h,0,sizeof(h));
		h[st]=1,d[1]=st;
		int l=0,r=1;
		while(l<r)
		{
			int k=d[++l];
			for(int i=fs[k];i;i=nt[i])
			{
				int p=dt[i];
				if(pr[i]&&!h[p]) 
				{
					h[p]=h[k]+1;
					d[++r]=p;
				}
			}
		}
		return (h[ed]>0);
	}	
	int dinic(int k,int s)
	{
		if(k==ed) return s;
		int sl=0,v;
		for(int i=fs[k];i;i=nt[i])
		{
			int p=dt[i];
			if(h[p]==h[k]+1&&pr[i])
			{
				if(v=dinic(p,min(s,pr[i])))
				{
					s-=v,sl+=v;
					pr[i]-=v,pr[fx[i]]+=v;
					if(!s) break;
				}
			}
		}
		if(!sl) h[k]=-1;
		return sl;
	}
}
using namespace flow;
int main()
{
	srand(130938325);
	cin>>m>>n;
	fo(i,1,m) 
	{
		scanf("%d%d%d",&ap[i][0],&ap[i][1],&ap[i][2]),ap[i][1]++;
		dw[2*i-1]=(px){ap[i][0],i,0};
		dw[2*i]=(px){ap[i][1],i,1};
		ans[i]=ap[i][2];
	}
	sort(dw+1,dw+2*m+1);
	
	fo(i,1,2*m)
	{
		if(i==1||dw[i].x!=dw[i-1].x) 
		{
			n1++;
			if(i%2==0) 
			{
				if(rand()&1) addedge(n1,n1-1,1);
				else addedge(n1-1,n1,1);
			}
		}
		ap[dw[i].w][dw[i].p]=n1;
	}
	st=n1+1,ed=n1+2;
	fo(i,1,m)
	{
		if(ap[i][2]<0) 
		{	
			ans[i]=rand()&1;
			if(ans[i]==0) addedge(ap[i][0],ap[i][1],1);
			else addedge(ap[i][1],ap[i][0],1);
			pf[i]=m1-1;
		}
		else
		{
			if(ans[i]==0) out[ap[i][0]]++,in[ap[i][1]]++;
			else out[ap[i][1]]++,in[ap[i][0]]++;
		}
	}
	int s1=0;
	fo(i,1,n1) 
	{
		if(in[i]<out[i]) s1+=(out[i]-in[i])/2,addedge(st,i,(out[i]-in[i])/2);
		if(in[i]>out[i]) addedge(i,ed,(in[i]-out[i])/2);
	} 
	while(bfs()) s1-=dinic(st,1e9);
	if(s1!=0) 
	{
		printf("-1\n");
		return 0;
	}
	fo(i,1,m)
	{
		if(ap[i][2]<0&&pr[pf[i]]==0) ans[i]^=1;
		printf("%d ",ans[i]); 
	}
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章