1. 程式人生 > 實用技巧 >Topcoder SRM568 Div1 DisjointSemicircles (二分圖染色)

Topcoder SRM568 Div1 DisjointSemicircles (二分圖染色)

Topcoder SRM568 Div1 DisjointSemicircles (二分圖染色)

題意: 給定數軸上排列的\(2n\)個點,每個點需要找到另一個點和它匹配,並且以他們為直徑兩端,向上或者向下作一個半圓

有一些點已經匹配好了,要求判斷是否存在一個合法的方案,滿足所有的半圓不相交

思路:

列舉已經確定的匹配半圓的方向(設有\(m\)對已匹配),然後\(O(n)\)判斷自由點是否存在合法方案

判斷合法方案的核心性質:

定義一個點的方向為其所連線的半圓的方向(上為0,下為1)

則自由點存在合法方案的充要條件是:

整個序列上每種方向的點數為偶數,且所有已匹配的半圓所覆蓋的區間下,和半圓同向的點

個數為偶數

必要性:

如果某個半圓下同向點個數為奇數,則必然有一個點與其同向並且不得不連到區間外,這顯然不合法

充分性:

一種合法的構造方法是:

按照\(L\)從左到右,遍歷每個已匹配的半圓,如果包含同向子半圓優先解決同向的子半圓

剩下的點依然是偶數個,從左到右依次和上一個匹配即可

\[\ \]

判斷是否存在合法方案

那麼問題轉化為判斷是否存在一種合法的定向方案,使得某一些區間裡0/1的個數為偶數

考慮構建二分圖染色,令點集\(V=\{0,1,\cdots,n,0',1',\cdots,n'\}\),則\((u,v)\in E\)表示\(col(u)\ne col(v)\)

其中\(i\)號節點表示\(1-i\)

中所有未匹配節點方向的異或和,\(i'\)表示\(i\)的反點\((i,i')\in E\)

(到這裡可以自己想一下怎麼連邊)

對於已匹配圓\([L,R]\) (注意不要忘了\([1,n]\))

如果它方向為\(1\),顯然只需要\(col(L-1)=col(R)\)

如果方向為0,設\([L,R]\)未染色個數為\(k\),則顯然有\(col(L-1)=col(R)\oplus (k\mod 2)\),即考慮反向的個數

同時對於已匹配點\(i\),顯然有\(col(i)=col(i-1)\)

由此,得到一個\(O(n)\)點數邊數的圖

如果在\(\text{dfs}\)列舉時同步加邊和回撤,總複雜度就為\(O(2^m\cdot n)\)

由於不可能所有方案都合法,實際應該是一個比較鬆的上界

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)

const int N=110;

int n;
int a[N];
int cnt[N];
int L[N],R[N],m,Cross[N][N];
int vec[2][N],c[2];

struct Edge{
	int u,v,nxt;
}e[N*10];
int head[N],ecnt;
void AddEdge(int u,int v) {
	e[++ecnt]=(Edge){u,v,head[u]};
	head[u]=ecnt;
}
void Link(int u,int v){ 
	AddEdge(u,v),AddEdge(v,u); 
}
void Back(){
	head[e[ecnt].u]=e[ecnt].nxt,ecnt--;
	head[e[ecnt].u]=e[ecnt].nxt,ecnt--;
}

int ans,fl;
int vis[N];
void dfs_col(int u,int c){
	vis[u]=c;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].v;
		if(!vis[v]) dfs_col(v,3-c);
		else if(vis[v]==vis[u]) fl=0;
	}
}

void dfs(int p) {
	if(ans) return;
	if(p>m){
		rep(i,0,n*2+1) vis[i]=0;
		fl=1;
		rep(i,0,n*2+1) if(!vis[i]) dfs_col(i,1);
		ans|=fl;
		return;
	}
	rep(i,0,1){
		int fl=1;
		rep(j,1,c[i]) if(R[vec[i][j]]>L[p] && R[vec[i][j]]<R[p]) fl=0;
		if(!fl) continue;
		vec[i][++c[i]]=p;
		if(i || ~(cnt[R[p]]-cnt[L[p]])&1) Link(L[p]+n+1,R[p]-1);
		else Link(L[p],R[p]-1);
		dfs(p+1);
		c[i]--,Back();
	}
}

class DisjointSemicircles {
public:
	string getPossibility(vector <int> labels) {
		n=labels.size(),m=0;
		rep(i,1,n) a[i]=labels[i-1];
		rep(i,1,n) {
			cnt[i]=cnt[i-1]+(a[i]==-1);
			if(~a[i]) rep(j,i+1,n) if(a[j]==a[i]) L[++m]=i,R[m]=j;
		}
		if(!m) return "POSSIBLE";
		rep(i,0,(n+1)*2) head[i]=ecnt=0;
		rep(i,1,n) if(~a[i]) Link(i+n+1,i-1);
		rep(i,0,n) Link(i,i+n+1);
		Link(n+1,n);
		ans=0,dfs(1);
		return ans?"POSSIBLE":"IMPOSSIBLE";
	}
};