• 欢迎光临~

SEERC2022 D Divisible by 4 Spanning Tree 题解

开发技术 开发技术 2022-12-28 次浏览

题意

给定 (n) 个点 (m) 条边的无向连通图,判断是否有存在生成树满足:度数为奇数的点个数为 (4) 的倍数。

(1le nle200000,1le mle400000)

题解

度数总和是 (2n-2),所以度数为奇数的点个数一定是偶数。

随便找一个生成树,如果满足条件就做完了。

否则需要判断奇数点个数模 (4) 能否发生变化。

这个限制看起来不是很紧,所以考虑大力讨论。

首先图上所有的桥是必选的,此时每个点度数已经有值了。

每个双连通分量是独立的,只要有一个能改变模 (4) 的值即可。

如果有连续的三个点 (x,y,z) 满足 (x,z) 奇偶不同则有解:

选择环上一条边断开等价于选择这条边上两点改变奇偶。

不妨令 (x,y) 奇偶相同,则 (y,z) 奇偶不同,贡献可以自由选择是 (0)(2)

两个环恰有一个交点

则两个环大小均不小于 (3),可以使用一个环选择是否改变公共点的奇偶,剩下的环贡献就是自由的。

此时一定有解。

两个环交于路径 ((u,v))

此时 (u,v) 之间恰好有三条路径,删边要求的是每条路径恰好删一条。

  • 当一条路径长度大于等于 (3)

这条路径上可以选中里面的边(和其它路径的点不交)或 (u) 作为端点的边,(u) 的奇偶是自由的。

于是剩下两条路径组成长度至少为 (4) 的环可以保证有解。

  • 路径长度为 ((2,1,2))

讨论发现可以改变任意两点奇偶,有解。

  • 路径长度为 ((2,2,2))

讨论打表发现仅有以下情况无解:

图可以表示成 ((1,2),(1,3),(1,4),(2,5),(3,4),(4,5)) ,且 (2,3,4) 奇偶相同,(1,5) 奇偶不同。

其它情况

显然包含合法子图,有解。

结论

当任意生成的生成树合法或所有双连通分量都不满足:

  • 是一个环且满足任意距离为 (2) 的点奇偶相同

  • 不是上文中 (5) 个点 (6) 条边的特定图

时有解。

时间复杂度:(O(n+m))

代码:

#include <bits/stdc++.h>
using namespace std;
bool solve(){
	int n,m,tot=0,k=0;
	scanf("%d%d",&n,&m);
	vector<int> st,dfn(n),low(n),bel(n),b(n),d(n);
	vector<vector<int>> e(n),p(n),g(n);
	for(int u,v;m--;)
		scanf("%d%d",&u,&v),u--,v--,e[u].push_back(v),e[v].push_back(u);
	auto dfs=[&](auto self,int x,int fa)->void{
		st.push_back(x);
		int&l=low[x];
		dfn[x]=l=++tot;
		for(int y:e[x])
			if(!dfn[y]){
				b[x]^=1;b[y]^=1;
				self(self,y,x);l=min(l,low[y]);
				if(low[y]>dfn[x]) d[x]^=1,d[y]^=1;
			}else if(y!=fa) l=min(l,dfn[y]);
		if(dfn[x]!=l) return;
		while(st.back()!=x) p[bel[st.back()]=k].push_back(st.back()),st.pop_back();
		p[bel[x]=k++].push_back(x);
		st.pop_back();
	};
	dfs(dfs,0,-1);
	if(accumulate(b.begin(),b.end(),0)%4==0) return 1;
	for(int x=0;x<n;x++) for(int y:e[x]) if(bel[x]==bel[y]) g[x].push_back(y);
	auto chk=[&](int id){
		int sz=p[id].size(),cnt=0;
		for(int x:p[id]) cnt+=g[x].size();
		cnt/=2;
		if(cnt<sz) return 0;
		if(cnt>sz&&!(cnt==6&&sz==5)) return 1;
		if(cnt==sz){
			for(int i=0;i<sz;i++) if(d[p[id][i]]^d[p[id][(i+2)%sz]]) return 1;
			return 0;
		}
		auto ok=[&](int x,int y){
			if(g[x].size()!=3||g[y].size()!=3||d[x]==d[y]||
			find(g[x].begin(),g[x].end(),y)!=g[x].end())
				return 0;
			int c=-1;
			for(int u:p[id]) if(u!=x&&u!=y){
				if(~c&&c!=d[u]) return 0;
				c=d[u];
			}
			return 1;
		};
		for(int i=0;i<sz;i++) for(int j=i+1;j<sz;j++) if(ok(p[id][i],p[id][j])) return 0;
		return 1;
	};
	for(int id=0;id<k;id++) if(chk(id)) return 1;
	return 0;
}
int main(){
	int t;scanf("%d",&t);
	while(t--) puts(solve()?"YES":"NO");
	return 0;
}
程序员灯塔
转载请注明原文链接:SEERC2022 D Divisible by 4 Spanning Tree 题解
喜欢 (0)