loj.#3077「2019 集训队互测 Day 4」绝目编诗 做题记录
奇怪的题目。
所有环只出现一次,似乎是只能暴搜才能做的事情。那么我们就直接暴搜。
枚举起点 \(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;
}

浙公网安备 33010602011771号