弦图学习笔记

弦:连接环中不想零的两个点的边

弦图:图中任意大小大于 3 的环都至少有一个弦

Fishing Net

判断弦图

Definitions

单纯点: \(u\)\(u\) 相邻点集构成的诱导子图是一个团

任意一个弦图至少有一个单纯点。非完全图的弦图至少有两个不相邻的单纯点

完美消除序列: 每次消除一个点,使得这个点在剩下的点的诱导子图中是一个单纯点

图是弦图 \(\Leftrightarrow\) 存在完美消除序列


Brute Force

每次寻找单纯点,然后删掉(\(O(n^4)\)

找完美消失序列

Algorithm 1 - Lex BFS

\(n\)\(1\) 给每个点标号。对于每一个点维护一个 list 记录已标号相邻点的标号,标号按字典序排序。每次选择 list 的字典序最大的未标号点标号。

Algorithm 2 - MCS

Maximum Cardinality Search 最大势算法

\(n\to 1\) 的顺序给每个点标号。设 \(l_i\) 表示第 \(i\) 个点与多少个已标号的店相邻。每次选择 \(l_i\) 最大的未标号点进行标号。

\(O(m+n)\)

判断是否是完美消失序列

暴力 \(O(mn)\)

优化:设 \(v_{i+1\dots n}\) 中与 \(v_i\) 相邻的点 \(w_1,w_2,\dots,w_k\)。只需要判断 \(w_1\)\(w_{2\dots k}\) 是否相邻即可\(O(m+n)\)

模板:SP5446 FISHNET - Fishing Net

int n,m,v[N],ed[N][N],tot,pes[N],pos[N],l[N],t[N];
bool vst[N];
vector<int>e[N];

struct node {
	int l,id;
	bool operator < (const node &y) const {return l<y.l;} 
};
void mcs() {
	memset(vst,0,sizeof(vst));
	priority_queue<node>q;
	rep(i,1,n) ed[i][i]=1, q.push((node){0,i});
	tot=n;
	while(!q.empty()) {
		int u=q.top().id; q.pop();
		if(!vst[u]) {
			pos[u]=tot, pes[tot--]=u, vst[u]=1;
			for(auto v:e[u]) if(!vst[v]) q.push((node){++l[v],v});
		}
	}
}
bool check() {
	memset(vst,0,sizeof(vst));
	per(i,n,1) {
		int u=pes[i], mp=n, cnt=0;
		vst[u]=1;
		for(auto v:e[u]) if(vst[v]) mp=min(mp,pos[t[++cnt]=v]);
		if(n==mp) continue;
		rep(j,1,cnt) if(!ed[pes[mp]][t[j]]) return 0;
	}
	return 1;
}

int main() {
	while((n=read())&&(m=read())) {
		memset(e,0,sizeof(e));
		memset(ed,0,sizeof(ed));
		rep(i,1,m) {
			int u=read(), v=read();
			e[u].push_back(v), e[v].push_back(u);
			ed[u][v]=ed[v][u]=1;
		}
		mcs();
		if(check()) puts("Perfect\n");
		else puts("Imperfect\n");
	}
	return 0;
}

其他问题

极大团

设第 \(i\) 个点在弦图的完美消失序列的 \(p_i\) 个。

\(N_v=\{w\mid (w,v)\in E 且 p_w>p_v\}\)

则弦图的极大团一定是 \(v\cup N_v\) 的形式。所以最多只有 \(n\) 个极大团。

接下来要判断 \(v\cup N_v\) 是否是极大团。

\(A=v\cup N_v\)\(B=w\cup N_w\)。则 \(A\) 不是极大团 \(\Leftrightarrow\) \(A\subset B\)

\(Next_v=\) \(N_v\) 中第一个点。

\[A\subset B \Leftrightarrow Next_w=v且|N_v|+1\le|N_w| \]

点染色

弦图的色数=团数(最大团大小)

例题:[HNOI2008]神奇的国度

int n,m,v[N],tot,pes[N],pos[N],l[N],t[N];
bitset<N>ed[N];
bool vst[N];
vector<int>e[N];

struct node {
	int l,id;
	bool operator < (const node &y) const {return l<y.l;} 
};
void mcs() {
	memset(vst,0,sizeof(vst));
	priority_queue<node>q;
	per(i,n,1) ed[i][i]=1, q.push((node){0,i});
	tot=n;
	while(!q.empty()) {
		int u=q.top().id; q.pop();
		if(!vst[u]) {
			pos[u]=tot, pes[tot--]=u, vst[u]=1;
			for(auto v:e[u]) if(!vst[v]) q.push((node){++l[v],v});
		}
	}
}
int max_cliq(int ret=0) {
	per(u,n,1) {
		int cnt=0;
		for(auto v:e[u]) if(pos[v]>pos[u]) cnt++;
		ret=max(ret,cnt+1);
	}
	return ret;
}

int main() {
	n=read(), m=read();
	rep(i,1,m) {
		int u=read(), v=read();
		e[u].push_back(v), e[v].push_back(u);
		ed[u][v]=ed[v][u]=1;
	}
	mcs();
	printf("%d\n",max_cliq());
	return 0;
}

最大独立集

完美消除序列从前往后能选就选

最小团覆盖

弦图的最小团覆盖=最大独立集

区间图

两个点右边当且仅当两个区间交集非空

区间图一定是弦图

区间图的完美消除序列是按照右端点从小到大排序

选择最多区间使得区间不重 $\to $ 区间图最大独立集

#define l(x) a[i].second
#define r(x) a[i].first

int n,ans;
pii a[N];

int main() {
	n=read();
	rep(i,1,n) l(i)=read(), r(i)=read();
	sort(a+1,a+n+1);
	int mxr=0;
	rep(i,1,n) if(mxr<l(i)) ans++, mxr=r(i);
	printf("%d\n",ans);
	return 0;
}
posted @ 2021-03-14 14:14  LarsWerner  阅读(139)  评论(0编辑  收藏  举报