【瞎口胡】弦图

弦图,我真的好喜欢你啊,为了你,我要求出你的完美消除序列!

本篇目参考 OI-Wiki,修改了部分证明。

定义#

弦图是满足以下条件的无向图:任意长度大于 3 的环上都有一条「」,即连接环上不相邻两点的边。

让我们明确以下新定义:

  • :完全子图。
  • 极大团:不是其它团子图的团。
  • 最大团:点数最大的团。
  • 团数:记作 ω(G),表示最大团点数。
  • 最小染色:使用最小的颜色对点进行染色使得相邻两点颜色不同。
  • 色数:记作 χ(G),表示最小染色使用的颜色数量。
  • 最大独立集:记作 α(G),最大的内部没有连边的点集。
  • 最小团覆盖:用最少的团覆盖所有点,使用的团的数量记为 κ(G)
  • 邻域:记作 N(x),表示与一个点相邻的点构成的集合。

基本性质#

  • 引理 1:团数 ω(G)χ(G) 色数

    考虑对最大团的导出子图进行染色,色数是 ω(G)

  • 引理 2:最大独立集 α(G)κ(G) 最小团覆盖数

    每个团最多选一个点。

  • 引理 3:弦图的导出子图一定是弦图。

    显然。

  • 引理 4:弦图的导出子图一定不是点数大于 3 的环。

    同引理 3。

点割集#

u,v 之间的点割集指的是一个点集,使得删掉它们后 u,v 不连通。最小点割集是满足条件的点集中,点数最小的。

  • 引理 5:考虑删掉最小点割集 S 之后,原图分成了至少两个联通块。设包含 u,v 的联通块分别是 V1,V2,那么对于任意 aS,一定存在 x,yN(a) 使得 xV1,yV2

  • 引理 6:最小点割集 S 一定是一个团。

    |S|1 时是平凡的。对于 |S|>1,此时最小点割集上一定存在两点 x,y,设 N(x),N(y)V1,V2 中的点分别为 x1,x2y1,y2

    点对 (x1,y1)(x2,y2) 之间有最短路。考虑 xy 在路径上只经过 V1,V2 时的最短路,不难发现分别为 xx1y1yxx2y2y,此时存在长度大于等于 4 的环 xx1y1yy2x2x,根据弦图的定义,该环上一定存在一条弦。

    注意到,弦不能连接 V1,V2 中各一个点(否则不是点割集);也不能连接 V1,V2 内部的两个点(否则不是最短路); 更不能连接 V1,V2 内部的一个点和 x,y 中的某一个(同理)。因此,这条弦只能连接 x,y

    于是对于任意 x,y 应用上述论述,我们得到了任意 x,y 之间都有边,于是它是一个团。

单纯点#

单纯点指的是满足以下条件的点 x:它和它的邻域,即 {x}+N(x)构成一个团。

  • 引理 7:任何一个弦图都有至少一个单纯点,不是完全图的弦图有至少两个不相邻的单纯点。

    考虑对于每个联通块单独证明。

    当图的大小 3 时,引理成立。

    如果图的大小至少为 4,完全图的部分也平凡的。接下来,如果图不是完全图,则一定存在一条边 (u,v)E。设 Iu,v 间的最小点割集,A,B 为删去 Iu,v 各自所在的联通块。

    不失一般性,考虑 A 一侧,设 L=A+I,如果 L 是完全图,则 u 是单纯点;如果不是,那么 L 上有两个不相邻的单纯点。因为 I 是完全图,其上任意两点相邻,因此 A 上至少有一个单纯点。

    考虑将 A 上的单纯点拓展到全图,如果邻域没有变化,那么它就是原图的一个单纯点。不难发现,A 上每个点的邻域一定是 L 中的一部分点,因此拓展到全图后邻域没有变化。

    对于 B 侧同样考虑,我们得到了两个不相邻的单纯点,引理 7 得证。

完美消除序列#

对于 n 个点的弦图,完美消除序列指的是这样一个序列 v1,v1,,vn,它满足对于每个 ivi{vi,vi+1,,vn} 的导出子图中是单纯点。

  • 引理 8:一个无向图是弦图当且仅当其存在完美消除序列。

    考虑如果一个无向图是弦图,每次在点数为 n1 的弦图的完美消除序列的最前方加入一个单纯点就可以得到点数为 n 的弦图的完美消除序列、

    如果一个非弦图的无向图存在大小大于 3 的环但有完美消除序列,设在完美消除序列中出现的第一个在环上的点是 v,它在环上和 v1,v2 相邻,根据单纯点的性质可知 v,v1,v2 构成一个团,于是存在弦 (v1,v2),矛盾。

  • MCS 最大势算法

    考虑从 n1 逆序给节点标号,设 lx 表示和 x 相邻的已标号点数的数量,每次选出 l 最大且未被标号的节点进行标号。正确性显然。

    一个图是弦图当且仅当求出的序列是完美消除序列。读者自证不难,我不会。

    时间复杂度 O(n+m)(使用链表)或者多一个 log

应用#

重新定义 N(x) 表示 x 的邻域中完美消除序列位置在自己之后的点的数量。

  • 找最大团

    最大的 {x}+N(x)

  • 色数

    按完美消除序列从后往前依次给每个点染色,给每个点染上可以染的最小颜色。

    考虑此时方案数 tχ(G),且瓶颈在于最大团上的染色,因此 t=ω(G),由引理 1得tχ(G),因此 t=χ(G)

    χ(G) 就是最大团大小。

  • 最大独立集 / 最小团覆盖

    最大独立集:完美消除序列从前往后,选择所有没有与已经选择的点有直接连边的点。

    最小团覆盖:独立集中的每个点和邻域构成的团,即 {{u1}+N(u1),{ut}+N(ut)}

    设上面的方法求出的答案为 t,那么 κ(G)tα(G),引理 2 得 α(G)κ(G),于是 α(G)=κ(G)=t

例题 1 [HNOI2008]神奇的国度#

题意

求弦图色数。

1n104.1m106

题解

按照结论模拟即可。

# include <bits/stdc++.h>

const int N=10010,INF=0x3f3f3f3f;

struct Node{
	int id,w;
	bool operator < (const Node &rhs) const{
		return w<rhs.w;
	}
};

std::priority_queue <Node> Q;
std::vector <int> G[N];
int n,m;
int rk[N],p[N],lb[N];

inline int read(void){
	int res,f=1;
	char c;
	while((c=getchar())<'0'||c>'9')
		if(c=='-') f=-1;
	res=c-48;
	while((c=getchar())>='0'&&c<='9')
		res=res*10+c-48;
	return res*f;
}

int main(void){
	n=read(),m=read();
	for(int i=1;i<=m;++i){
		int u=read(),v=read();
		G[u].push_back(v),G[v].push_back(u);
	}
	for(int i=1;i<=n;++i) Q.push((Node){i,0});
	int cur=n;
	while(!Q.empty()){
		int i=Q.top().id;
		Q.pop();
		if(rk[i]) continue;
		rk[i]=cur,p[cur]=i,--cur;
		for(auto v:G[i]){
			if(!rk[v]) ++lb[v],Q.push((Node){v,lb[v]});
		}
	} // MCS 算法
	int mx=0;
	for(int i=1;i<=n;++i){
		int cur=1;
		for(auto v:G[i]) if(rk[i]<rk[v]) ++cur;
		mx=std::max(mx,cur);
	}
	printf("%d",mx);
	return 0;
}

例题 2 [TJOI2007]小朋友#

题意

求弦图最大独立集。

1n200,0mn(n1)2

题解

按照题意模拟即可。

# include <bits/stdc++.h>

const int N=210,INF=0x3f3f3f3f;

struct Node{
	int id,w;
	bool operator < (const Node &rhs) const{
		return w<rhs.w;
	}
};

std::priority_queue <Node> Q;
std::vector <int> G[N];
int n,m;
int rk[N],p[N],lb[N];
bool vis[N];

inline int read(void){
	int res,f=1;
	char c;
	while((c=getchar())<'0'||c>'9')
		if(c=='-') f=-1;
	res=c-48;
	while((c=getchar())>='0'&&c<='9')
		res=res*10+c-48;
	return res*f;
}

int main(void){
	n=read(),m=read();
	for(int i=1;i<=m;++i){
		int u=read(),v=read();
		G[u].push_back(v),G[v].push_back(u);
	}
	for(int i=1;i<=n;++i) Q.push((Node){i,0});
	int cur=n;
	while(!Q.empty()){
		int i=Q.top().id;
		Q.pop();
		if(rk[i]) continue;
		rk[i]=cur,p[cur]=i,--cur;
		for(auto v:G[i]){
			if(!rk[v]) ++lb[v],Q.push((Node){v,lb[v]});
		}
	}
	int mx=0;
	for(int i=1;i<=n;++i){
		if(vis[p[i]]) continue;
		++mx,vis[p[i]]=true;
		for(auto v:G[p[i]]) if(rk[p[i]]<rk[v]) vis[v]=true;
	}
	printf("%d",mx);
	return 0;
}

作者:Meatherm

出处:https://www.cnblogs.com/Meatherm/p/16626612.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Meatherm  阅读(206)  评论(2编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
more_horiz
keyboard_arrow_up light_mode palette
选择主题
menu
点击右上角即可分享
微信分享提示