【模板】弦图判定&最小染色(MCS)

弦图与区间图总结-DZYO(定义)
弦图 完美消除序列 MCS算法(代码)

省选前为什么要开这种奇奇怪怪的知识点
具体定义和证明较略,主要记录实现方法。


定义

弦就是连接环上两个不相邻点的边。弦图满足图中任意长度>3的环都至少有一个弦(递归下去就是一个三角剖分的图)

单纯点:
v v v是单纯点当且仅当 v v v和其相邻的点集 N ( v ) N(v) N(v)构成的原图的诱导子图是完全图。

性质

普通图:
最大团数 ≤ \leq 最小色数
最大独立集 ≤ \leq 最小团覆盖

弦图:
最大团数=最小色数
最大独立集=最小团覆盖

完美消除序列:
原图的一个点序列 v 1 , v 2 , . . . , v n v_1,v_2,...,v_n v1,v2,...,vn(每个点都出现恰好一次),满足 v i v_i vi v i , v i + 1 , . . . , v n v_i,v_{i+1},...,v_n vi,vi+1,...,vn的诱导子图中为一个单纯点。

定理:一个无向图是弦图当且仅当它有一个完美消除序列。


判定

由定理:“一个无向图是弦图当且仅当它有一个完美消除序列”。
考虑求出一个序列并验证其是否为完美消除序列。

最大势算法MCS

倒序求出 v n , . . . , v 1 v_n,...,v_1 vn,...,v1(逐个标出 n − 1 n-1 n1),设 f i f_i fi表示点 i i i与多少个已标号点相邻,每次选 f i f_i fi最大的未标号点标号。

验证是否为完美消除序列:

v i + 1 , . . . , v n v_{i+1},...,v_n vi+1,...,vn中与 v i v_i vi相邻的下标最小( v v v中排列最靠前)点为 v j v_j vj,只需要判断 v j v_j vj是否与其后所有与 v i v_i vi相邻的点相邻即可。

复杂度 O ( n + m ) O(n+m) O(n+m)
n n n比较小时,可以邻接矩阵判边的存在性。

//bzoj1242 Zju1015 Fishing Net弦图判定
#include
#define pb push_back
using namespace std;
const int N=1010,M=4e6+10;

int n,m,g[N][N],q[N],f[N],rk[N];
int head[N],to[M],nxt[M],tot;
vector<int>nt[N];bool vs[N];

inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}

void MSC()
{
	int i,j,x,y,bst=0;
	for(i=1;i<=n;++i) lk(0,i);
	for(j=n;j>0;--j){
		for(;;){
			for(int &i=head[bst];i;i=nxt[i])
				if(!vs[to[i]]) break;
			if(head[bst]){
				x=to[head[bst]];q[j]=x;rk[x]=j;vs[x]=true;
				for(i=nt[x].size()-1;i>=0;--i) if(!vs[nt[x][i]]){
					y=nt[x][i];f[y]++;lk(f[y],y);
					bst=max(bst,f[y]);
				}
				break;
			}else bst--;
		}
	}
}

int stk[N],top;

bool ck()
{
	int i,j,x,y,mng,mnv;
	for(j=n;j>0;--j){
		x=q[j];top=0;mng=n+1;
		for(i=nt[x].size()-1;i>=0;--i) if(rk[nt[x][i]]>j){
			y=nt[x][i];stk[++top]=y;
			if(rk[y]<mng) mng=rk[y],mnv=y; 
		}
		if(mng==n+1) continue;
		for(i=1;i<=top;++i) if(stk[i]!=mnv)
		 if(!g[mnv][stk[i]]) return false;
	}
	return true;
}

int main(){
	int i,j,x,y;
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;++i){
		scanf("%d%d",&x,&y);
		g[x][y]=g[y][x]=1;
		nt[x].pb(y);nt[y].pb(x);
	}
	MSC();
	if(ck()) printf("Perfect");
	else printf("Imperfect");
	return 0;
}

最小染色

同样MCS求出完美消除序列

倒序处理 v n , . . . , v 1 v_n,...,v_1 vn,...,v1的染色,求出最小的没有被 v i v_i vi已染色相邻点( v v v中排名比它大)取的颜色 k k k,设 c o l [ v i ] = k col[v_i]=k col[vi]=k即可。

//bzoj1006 [HNOI2008]神奇的国度 弦图最小染色 
#include
#define pb push_back
using namespace std;
const int N=10010,M=4e6+10;

int n,m,q[N],f[N],ans,col[N],tg[N];
int head[N],to[M],nxt[M],tot;
vector<int>nt[N];bool vs[N];

inline void lk(int u,int v)
{to[++tot]=v;nxt[tot]=head[u];head[u]=tot;}

void MSC()
{
	int i,j,x,y,bst=0;
	for(i=1;i<=n;++i) lk(0,i);
	for(j=n;j>0;--j){
		for(;;){
			for(int &i=head[bst];i;i=nxt[i])
				if(!vs[to[i]]) break;
			if(head[bst]){
				x=to[head[bst]];q[j]=x;vs[x]=true;
				for(i=nt[x].size()-1;i>=0;--i) if(!vs[nt[x][i]]){
					y=nt[x][i];f[y]++;lk(f[y],y);
					bst=max(bst,f[y]);
				}
				break;
			}else bst--;
		}
	}
}

int main(){
	int i,j,x,y;
	scanf("%d%d",&n,&m);
	for(i=1;i<=m;++i){
		scanf("%d%d",&x,&y);
		nt[x].pb(y);nt[y].pb(x);
	}
	MSC();
	for(j=n;j>0;--j){
		x=q[j];
		for(i=nt[x].size()-1;i>=0;--i) tg[col[nt[x][i]]]=x;
		for(i=1;tg[i]==x;++i);col[x]=i;ans=max(ans,i);
	}
	printf("%d",ans);
	return 0;
} 

你可能感兴趣的:(弦图(MCS))