[2019 集训队互测 Day4] 绝目编诗

一、题目

点此看题

二、解法

谢谢这个大佬的博客,讲得真的挺好的。

做的时候脑抽了,连暴搜都不会,我们先来考察一种有前途的暴搜。

首先枚举起点,然后尝试搜出一条到起点的回路(就是最暴力的方法,枚举下一个点然后回溯)。搜到每一个点的时候判断,如果它没有构成环的可能(即不通过在搜索栈中的节点到达起点),那么直接退出。

分析一下这种方法的时间复杂度,首先根据抽屉原理,环的个数不超过 \(n\) 个,最坏的情况是 \(3\sim n\) 各分布一个。考虑一个长度为 \(i\) 个环会被搜到 \(2i\) 次,每次搜到都需要走 \(i\) 个点,所以是 \(\sum_{i=3}^n2i^2=O(n^3)\);此时再乘上检查的复杂度,那么我们得到了一个稳定 \(O(n^4)\) 的算法。稍加随机化和掐表随便过前四个包,甚至可能直接过掉本题

有一个神仙结论是 \(m>n+2\sqrt n\) 一定有解,考虑随机删 \(\sqrt n\) 条边期望剩下的环个数:

\[\sum_{i=3}^n(1-\frac{1}{\sqrt n})^i<\frac{1}{1-(1-\frac{1}{\sqrt n})}=\sqrt n \]

那么一定存在一种删边使得剩下环的个数 \(\leq \sqrt n\),我们再用 \(\sqrt n\) 次就一定能删完所有环。换句话说,我们用 \(2\sqrt n\) 次删除可以让这个图至多剩下 \(n-1\) 条边,那么原图的边数一定不超过 \(n+2\sqrt n\)

考虑原图的一棵 \(\tt dfs\) 树,我们把非树边标记成关键点,然后建立关键点的虚树,再添加上非树边就得到了一个点数边数都是 \(O(\sqrt n)\) 级别的图。在这个图上找环就等价于在原图上找环,一开始介绍的 \(O(n^4)\) 方法是可以支持带权边找环的,直接套用它,时间复杂度 \(O(n^2)\)

#include <cstdio>
#include <vector>
#include <cstdlib>
#include <iostream>
#include <cmath>
using namespace std;
const int M = 10005;
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,tot=1,f[M],zz[M];vector<int> g[M];
int p[M],vis[M],dep[M],dis[M],len[M],cnt[M];
struct edge{int v,c,next;}e[M<<1];
void add(int u,int v,int c)
{
	e[++tot]=edge{v,c,f[u]},f[u]=tot;
	e[++tot]=edge{u,c,f[v]},f[v]=tot;
}
void dfs(int u,int fa)
{
	int son=0;
	dep[u]=dep[fa]+1;zz[u]=fa;
	for(int v:g[u])
	{
		if(v==fa) continue;
		if(dep[v])
		{
			if(dep[u]<dep[v]) continue;
			if(!p[u]) p[u]=u;
			if(!p[v]) p[v]=v;
			add(p[u],p[v],1);
			continue;
		}
		dfs(v,u);
		if(p[v]) son=son?-1:p[v];
	}
	if(!p[u] && son<0) p[u]=u;
	if(p[u])
	{
		for(int v:g[u]) if(zz[v]==u && p[v])
			add(p[u],p[v],dep[p[v]]-dep[p[u]]);
	}
	else p[u]=son;
}
int check(int u,int nw)
{
	if(!dep[u]) return 1;vis[u]=nw;
	for(int i=f[u];i;i=e[i].next)
	{
		int v=e[i].v;
		if(vis[v]==nw || dep[v]>0) continue;
		if(check(v,nw)) return 1;
	}
	return 0;
}
void work(int u,int fa)
{
	if(!check(u,++k)) return ;
	for(int i=f[u];i;i=e[i].next)
	{
		int v=e[i].v,c=e[i].c,r=c+dis[u];
		if(i==fa) continue;
		if(!dep[v])//find root
		{
			if(!len[r]) len[r]=dep[u]+1;
			if(len[r]!=dep[u]+1) {puts("Yes");exit(0);}
			cnt[r]++;
			if(cnt[r]>2*len[r]) {puts("Yes");exit(0);}
		}
		if(dep[v]>=0) continue;
		dep[v]=dep[u]+1;dis[v]=r;
		work(v,i^1);dep[v]=-1;
	}
}
signed main()
{
	n=read();m=read();
	if(m>n+2*sqrt(n)) {puts("Yes");return 0;}
	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++)
		if(!dep[i]) dfs(i,0);
	for(int i=1;i<=n;i++) dep[i]=-1;
	for(int i=1;i<=n;i++) if(p[i]==i)
		dep[i]=dis[i]=0,work(i,0),dep[i]=-1;
	puts("No");
}
posted @ 2022-06-12 15:32  C202044zxy  阅读(214)  评论(0编辑  收藏  举报