[JZOJ6231] 【NOI2019模拟6.25】等你哈苏德【图论】【欧拉回路】【网络流】

37 篇文章 0 订阅
25 篇文章 0 订阅

Description

数轴上有一些线段,需要将它们染成黑或白色,有些已经染好了颜色,现在求一种染色方案使得对于所有整点,覆盖它的黑色线段和白色线段数之差的绝对值不超过1

n &lt; = 30000 n&lt;=30000 n<=30000

Solution

我们把白色看做+1,黑色看做-1,问题变成要求每个位置的值只能是 [ − 1 , 0 , 1 ] [-1,0,1] [1,0,1]

由于是区间加减,我们考虑差分变成两个点的加和减
因此可以将左端点和右端点+1拉出来离散化,只有这些点是有用的
一个点加,一个点减,我们将两个点之间连一条有向边,那么就意味着一个点出度+1,一个点入度+1

假定要求黑色线段和白色线段数之差要等于0,那么就是所有的点入度=出度,它存在一条欧拉回路,我们只需要想办法让所有点入度=出度即可。

这实际上就是混合图的欧拉回路问题,我们将未定向的边随机定向,新建源点汇点,源点向出度大于入度的点连容量为(出度-入度)/2,反之连向汇点,原图的边容量全为1,跑一遍最大流,一条增广路就意味着将路径上所有边反向,两个出入度不等的点调整了一下,且中间的所有点不改变,合法的条件就是新边全部满流。

如果我们随机定向的边被流过了说明这条边的方向定反了。
注意已经有方向的边要计算度数,但不能在网络中连这条边(因为不能修改)

回归原问题,我们要求绝对值不超过1,也就是说我们可以补上一些边使得它存在欧拉回路。

如果直接将度数为奇数的点拉出来连边是错误的
我们考虑这个东西-1,1,0,它差分的结果是-1,2,-1

2是偶点,它不会被连边,但它并不能构成欧拉回路。
实际上2应该向两个-1各连一条边,而不是-1之间连起来。

于是我们修改补边办法,将所有关键点拉出来不去重的排序,第 2 i − 1 2i-1 2i1个点与第 2 i 2i 2i个点之间连一条随机定向的边,这样在保证了所有点都是偶点的情况下也满足了上面的情况(显然不可能连续两个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]); 
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值