Loading

loj.#3077「2019 集训队互测 Day 4」绝目编诗 做题记录

link

奇怪的题目。

所有环只出现一次,似乎是只能暴搜才能做的事情。那么我们就直接暴搜。

枚举起点 \(s\) 进行搜索,当搜出一个环时记录下环长,设 \(c_i\) 表示搜出长度为 \(i\) 的环的次数。

对于长度为 \(i\) 的环,有 \(i\) 个可能的起点,\(2\) 种不同方向,会搜出 \(2i\) 次。所以若 \(c_i > 2i\) 则答案为 Yes。

我们希望有一个准确的时间复杂度。考虑每个环搜索出来的时间复杂度为 \(\mathcal O(n)\),有 \(n\) 种环,每种环会搜出 \(\mathcal O(n)\) 次,所以我们希望搜索的复杂度为 \(\mathcal O(n^3)\)

这要求当前搜索出来的路径必须在某一个环上,可以另加一个 DFS 用于判断,这样总时间复杂度为 \(\mathcal O(n^2m^2)\)

发现 \(m\) 过大,考虑对于一棵森林,每加入一条非树边至少产生一个环。长为 \(3\sim n\) 的环共 \(n - 2\) 个,所以若 \(m > 2n - 2\) 则答案为 Yes。

此时 \(m\)\(n\) 同级,复杂度为 \(\mathcal O(n^4)\)

接下来是魔法:

考虑继续缩紧 \(m\) 的上界。对于原图,若每条边有 \(\frac 1 {\sqrt n}\) 的概率被删除,那么长度为 \(i\) 的环保留的概率为 \((1 - \frac 1{\sqrt n}) ^ i\)

那么剩余环数量的期望值为 \(\sum\limits_{i = 3} ^ n (1 - \frac 1{\sqrt n}) ^ i = \dfrac {(1 - \frac 1{\sqrt n}) ^ 3 - (1 - \frac 1{\sqrt n}) ^ {n + 1}} {\frac 1{\sqrt n}} < \sqrt n\)

那么再删 \(\mathcal O(\sqrt n)\) 条边即可使原图不存在环,所以当 \(m > n + 2\sqrt n\) 时答案为 Yes。

所以非树边至多 \(2\sqrt n\) 条。考虑求出这些非树边关键点的虚树,并连上非树边形成一张新图。新图的点数为 \(\mathcal O(\sqrt n)\),再进行搜索即可做到 \(\mathcal O(n^2)\)

  • 启示:暴搜思想;时间复杂度过大时可以观察性质缩紧上界。
点击查看代码
#include <bits/stdc++.h>
namespace Initial {
	#define ll int
	#define ull unsigned ll
	#define fi first
	#define se second
	#define mkp make_pair
	#define pir pair <ll, ll>
	#define pb push_back
	#define i128 __int128
	using namespace std;
	const ll maxn = 4e5 + 10, inf = 1e9, mod = 998244353, iv = mod - mod / 2;
	ll power(ll a, ll b = mod - 2, ll p = mod) {
		ll s = 1;
		while(b) {
			if(b & 1) s = 1ll * s * a %p;
			a = 1ll * a * a %p, b >>= 1;
		} return s;
	}
	template <class T>
	const inline ll pls(const T x, const T y) { return x + y >= mod? x + y - mod : x + y; }
	template <class T>
	const inline void add(T &x, const T y) { x = x + y >= mod? x + y - mod : x + y; }
	template <class T>
	const inline void chkmax(T &x, const T y) { x = x < y? y : x; }
	template <class T>
	const inline void chkmin(T &x, const T y) { x = x < y? x : y; }
} using namespace Initial;

namespace Read {
	char buf[1 << 22], *p1, *p2;
	// #define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, (1 << 22) - 10, stdin), p1 == p2)? EOF : *p1++)
	template <class T>
	const inline void rd(T &x) {
		char ch; bool neg = 0;
		while(!isdigit(ch = getchar()))
			if(ch == '-') neg = 1;
		x = ch - '0';
		while(isdigit(ch = getchar()))
			x = (x << 1) + (x << 3) + ch - '0';
		if(neg) x = -x;
	}
} using Read::rd;

ll n, m; vector <ll> to[maxn], g[maxn], ver, _g[maxn];
bool ind[maxn], vis[maxn]; ll stk[maxn], top;
ll dfn[maxn], ti, d[maxn][20], dep[maxn], s;

void dfs1(ll u, ll fa = 0) {
	d[u][0] = fa, dep[u] = dep[fa] + 1; dfn[u] = ++ti;
	vis[u] = true;
	for(ll i = 1; i < 20; i++) d[u][i] = d[d[u][i - 1]][i - 1];
	for(ll v: to[u])
		if(v ^ fa) {
			if(!vis[v]) dfs1(v, u);
			else if(dep[v] < dep[u])
				ind[u] = ind[v] = true, _g[u].pb(v), _g[v].pb(u);
		}
}
ll lca(ll u, ll v) {
	if(dep[u] < dep[v]) swap(u, v);
	ll t = dep[u] - dep[v];
	for(ll i = 0; i < 20; i++)
		if(t & (1 << i)) u = d[u][i];
	if(u == v) return u;
	for(ll i = 19; ~i; i--)
		if(d[u][i] ^ d[v][i])
			u = d[u][i], v = d[v][i];
	return d[u][0];
}

void link(ll u, ll v) { g[u].pb(v), g[v].pb(u); }

bool fvis[maxn]; ll cnt, sum, kk[maxn];
bool check(ll u) {
	fvis[u] = true;
	for(ll v: g[u])
		if(v == s || !vis[v] && !fvis[v] && check(v)) return true;
	for(ll v: _g[u])
		if(v == s || !vis[v] && !fvis[v] && check(v)) return true;
	return false;
}

void dfs2(ll u, ll fa = 0, ll col = 0) {
	vis[u] = true;
	memset(fvis, false, sizeof fvis);
	if(!check(u)) return vis[u] = false, void();
	for(ll v: g[u]) {
		if(col == 0 && v == fa) continue;
		cnt++, sum += abs(dep[v] - dep[u]);
		if(v == s) {
			++kk[sum];
			if(kk[sum] > 2 * cnt) puts("Yes"), exit(0);
		} else if(!vis[v]) dfs2(v, u, 0);
		cnt--, sum -= abs(dep[v] - dep[u]);
	}
	for(ll v: _g[u]) {
		if(col == 1 && v == fa) continue;
		cnt++, sum++;
		if(v == s) {
			++kk[sum];
			if(kk[sum] > 2 * cnt) puts("Yes"), exit(0);
		} else if(!vis[v]) dfs2(v, u, 1);
		cnt--, sum--;
	}
	vis[u] = false;
}

int main() {
	rd(n), rd(m);
	if(m > n + 2 * sqrt(n)) return puts("Yes"), 0;
	for(ll i = 1; i <= m; i++) {
		ll u, v; rd(u), rd(v);
		to[u].pb(v), to[v].pb(u);
	}
	for(ll i = 1; i <= n; i++)
		if(!vis[i]) dfs1(i);
	ind[1] = true, stk[top = 1] = 1;
	for(ll i = 2; i <= n; i++)
		if(ind[i]) ver.pb(i);
	sort(ver.begin(), ver.end(), [](ll x, ll y) {return dfn[x] < dfn[y];});
	for(ll u: ver) {
		ll c = lca(stk[top], u);
		if(c ^ stk[top]) {
			while(dfn[stk[top - 1]] > dfn[c]) link(stk[top - 1], stk[top]), --top;
			link(c, stk[top]);
			if(stk[top - 1] ^ c) stk[top] = c;
			else --top;
		} stk[++top] = u;
	}
	while(top > 1) link(stk[top], stk[top - 1]), --top;
	memset(vis, false, sizeof vis);
	for(s = 1; s <= n; s++)
		if(ind[s]) dfs2(s);
	puts("No");
	return 0;
}
posted @ 2025-02-09 19:22  Sktn0089  阅读(64)  评论(0)    收藏  举报