Description
數軸上有一些線段,需要將它們染成黑或白色,有些已經染好了顏色,現在求一種染色方案使得對於所有整點,覆蓋它的黑色線段和白色線段數之差的絕對值不超過1
Solution
我們把白色看做+1,黑色看做-1,問題變成要求每個位置的值只能是
由於是區間加減,我們考慮差分變成兩個點的加和減
因此可以將左端點和右端點+1拉出來離散化,只有這些點是有用的
一個點加,一個點減,我們將兩個點之間連一條有向邊,那麼就意味着一個點出度+1,一個點入度+1
假定要求黑色線段和白色線段數之差要等於0,那麼就是所有的點入度=出度,它存在一條歐拉回路,我們只需要想辦法讓所有點入度=出度即可。
這實際上就是混合圖的歐拉回路問題,我們將未定向的邊隨機定向,新建源點匯點,源點向出度大於入度的點連容量爲(出度-入度)/2,反之連向匯點,原圖的邊容量全爲1,跑一遍最大流,一條增廣路就意味着將路徑上所有邊反向,兩個出入度不等的點調整了一下,且中間的所有點不改變,合法的條件就是新邊全部滿流。
如果我們隨機定向的邊被流過了說明這條邊的方向定反了。
注意已經有方向的邊要計算度數,但不能在網絡中連這條邊(因爲不能修改)
迴歸原問題,我們要求絕對值不超過1,也就是說我們可以補上一些邊使得它存在歐拉回路。
如果直接將度數爲奇數的點拉出來連邊是錯誤的
我們考慮這個東西-1,1,0,它差分的結果是-1,2,-1
2是偶點,它不會被連邊,但它並不能構成歐拉回路。
實際上2應該向兩個-1各連一條邊,而不是-1之間連起來。
於是我們修改補邊辦法,將所有關鍵點拉出來不去重的排序,第個點與第個點之間連一條隨機定向的邊,這樣在保證了所有點都是偶點的情況下也滿足了上面的情況(顯然不可能連續兩個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]);
}
}