统一省选 2023 D1T2 城市建造
显然地,这 \(t\) 座城市一定由每个连通块出一座得来,换言之,新修建道路的两城市原来一定不连通。
进一步可以想到,若选择了 \(u, v\) 两座城市且它们连通,则 \(u \rightsquigarrow v\) 上的所有城市都应被选择。
更进一步地可以推出,若选择的城市同属一个点双,则该点双内的所有城市都应被选择。
对给定的图建出圆方树,定义一次 选择 操作为:选择该该点双内的所有城市(对方点),那么有:
- 选择的方点构成一个连通块。
- 删去选择的方点后,剩余的所有连通块大小(圆点数量)的极差不超过 \(k\)。
接下来分类考虑解法。
当 \(k = 0\) 时:
枚举每个连通块的大小 \(s(s | n)\)。
首先,必定有树的重心 \(o \in V'\),于是考虑以 \(o\) 为根 树形 DP。
证明:
若 \(o \notin V'\),则 \(g\) 所在的连通块的大小一定 \(> \dfrac n2\),无论 \(k\) 怎么取都不符合题意。
设 \(f(u)\) 表示 \(u\) 所对应的方点能否被删除,每个连通块的大小为 \(s\) 成立,则一定有 \(f(o) = 1\)。
- 对圆点,若 \(\exist v \in son_u, sz_v \ge s\),则 \(f(u) = 1\),否则 \(f(u) = 0\)。额外地,若 \(f(u) = 0\),则要求 \(\sum\limits_{v \in son_u} sz_v + 1 = s\)。
- 对方点,\(f(u) = \prod\limits_{v \in son_o} f(v)\)
时间复杂度 \(\mathcal O[nd(n)]\)。
当 \(k = 1\) 时:
枚举 \(s \in [1, \lfloor \dfrac n2 \rfloor]\),则每个连通块的大小 \(\in [s, s + 1]\)。
设 \(f(u)\) 表示以 \(u\) 为根的子树中的方案数,\(v \in son_u\)。
-
对圆点,
- 若 \(sz_v < s\),则 \(v\) 一定不能删去。
- 若 \(sz_v > s\),则 \(v\) 一定要删去。
- 否则有 \(sz_v = s\),那么当 \(f(v) = 0\) 时,\(v\) 一定不能删去,否则 \(v\) 可删可不删,额外地,若不删 \(v\),则需要满足不存在未被删去的 \(v'\)。
设 \(x\) 为所有可删的 \(\prod f(v)\), \(y\) 为必须删去的 \(\sum sz(v) + 1\):
- 若 \(y \in [k, k + 1]\),则 \(f(u) \gets x\)。
- 若 \(y = 1\),则 \(f(u) \gets \sum\limits_{sz_v = s \and f(v) > 0} \dfrac x{f(v)}\),又因为满足条件的 \(f(v)\) 势必为 \(1\),所以令 \(T = \sum[sz_v = s \and f(v) > 0]\),则有 \(f(u) \gets xT\)。
-
对方点,\(f(u) = \prod f(v)\)。
最后注意到有算重的情况,需要容斥减掉 \(k = 0\) 的 \(s \in [2, \lfloor \dfrac n2 \rfloor]\)。
时间复杂度 \(\mathcal O[n^2 + nd(n)]\)。
考虑优化,瓶颈在 \(k = 1\) 的情况。
发现对答案有贡献的 \(s\) 必然满足 \(n \bmod s \le \lfloor \dfrac ns \rfloor\),将 \(n \bmod s\) 替换为 \(n - s\lfloor \dfrac ns \rfloor\) 后可以得到 \(n \le (s + 1)\lfloor \dfrac ns \rfloor\),计算这些 \(s\) 即可。
时间复杂度 \(\mathcal O(n \sqrt n)\)。
有点卡常,加点剪枝:
- 若 \(sz_u < s\) 则无需递归(一定无法删去)。
- 若 \(sz_u > s(k = 0)\) 或 \(sz_u > s + 1(k = 1)\) 则直接返回 \(0\)(显然一定无解)。
代码:
#include <bits/stdc++.h>
using namespace std;
template<typename _T>
void read(_T &_x) {
_x = 0;
_T _f = 1;
char _ch = getchar();
while (_ch < '0' || '9' < _ch) {
if (_ch == '-') _f = -1;
_ch = getchar();
}
while ('0' <= _ch && _ch <= '9') {
_x = (_x << 3) + (_x << 1) + (_ch & 15);
_ch = getchar();
}
_x *= _f;
}
template<typename _T>
void write(_T _x) {
if (_x < 0) {
putchar('-');
_x = -_x;
}
static int _stk[128]; int _top = 0;
do {
_stk[_top++] = _x % 10;
_x /= 10;
} while (_x);
while (_top) putchar('0' + _stk[--_top]);
}
typedef long long ll;
const int MAXN = 2e5 + 10, MOD = 998244353;
int n, m, k;
int cnt, top, p, dfn[MAXN], id[MAXN], low[MAXN], stk[MAXN];
int o, sz[MAXN << 1], mx[MAXN << 1], f[MAXN << 1];
bool vis[MAXN];
vector<int> E[MAXN], G[MAXN << 1], H[MAXN << 1];
inline void addE(int u, int v) {
E[u].emplace_back(v), E[v].emplace_back(u);
}
inline void addG(int u, int v) {
G[u].emplace_back(v), G[v].emplace_back(u);
}
void tarjan(int u) {
dfn[u] = low[u] = ++cnt, stk[++top] = u;
for (int v : E[u]) {
if (!dfn[v]) {
tarjan(v), low[u] = min(low[u], low[v]);
if (low[v] == dfn[u]) {
addG(u, ++p);
for (int x = 0; x != v;) addG(x = stk[top--], p);
}
} else low[u] = min(low[u], dfn[v]);
}
}
void dfs1(int u, int fa) {
sz[u] = u <= n;
for (int v : G[u]) {
if (v == fa) continue;
dfs1(v, u); sz[u] += sz[v];
}
mx[u] = max(sz[u], n - sz[u]);
}
void dfs2(int u, int fa) {
id[++id[0]] = u, sz[u] = u <= n;
for (int v : G[u]) {
if (v == fa) continue;
H[u].emplace_back(v); dfs2(v, u); sz[u] += sz[v];
}
}
int dp0(int s) {
for (int i = p; i; i--) {
int u = id[i];
if (sz[u] < s) continue;
if (u <= n) {
f[u] = 0;
int sum = 1; bool flag = 1;
for (int v : H[u]) {
if (sz[v] < s) sum += sz[v];
else flag &= f[v];
}
f[u] = sum == s && flag;
} else {
f[u] = 1;
for (int v : H[u]) {
if (sz[v] < s) f[u] = 0;
else if (!f[v]) f[u] = 0;
}
}
if (sz[u] > s + 1 && f[u] == 0) return 0;
}
return 1;
}
int dp1(int s) {
for (int i = p; i; i--) {
int u = id[i];
if (sz[u] < s) continue;
if (u <= n) {
f[u] = 0;
ll x = 1; int y = 1, T = 0;
for (int v : H[u]) {
if (sz[v] < s) y += sz[v];
else if (sz[v] == s) f[v] ? T++ : y += s;
else x = x * f[v] % MOD;
}
if (y == s || y == s + 1) f[u] = x;
if (y == 1) f[u] = (f[u] + x * T) % MOD;
} else {
f[u] = 1;
for (int v : H[u]) {
if (sz[v] < s) {
f[u] = 0; break;
}
f[u] = 1ll * f[u] * f[v] % MOD;
}
}
if (sz[u] > s + 1 && !f[u]) return 0;
}
return f[o];
}
int main() {
read(n), read(m), read(k); p = n;
while (m--) {
int u, v; read(u), read(v); addE(u, v);
}
tarjan(1), dfs1(1, 0);
int mn = n;
for (int i = 1; i <= n; i++) if (mx[i] < mn) mn = mx[i], o = i;
dfs2(o, 0); int lim = sqrt(n), ans = 0;
if (k == 0) {
for (int i = 2; i <= lim; i++) {
if (n % i) continue;
ans += dp0(i) + dp0(n / i);
}
write(ans + 1);
} else {
for (int i = 2; i <= n; i++) {
vis[n / i] = 1;
if (n % i == 0) vis[n / i - 1] = 1;
}
for (int i = 1; i <= n; i++) if (vis[i]) (ans += dp1(i)) %= MOD;
for (int i = 2; i <= lim; i++) if (n % i == 0) ans -= dp0(i) + (i * i != n) * dp0(n / i);
write(ans);
}
return 0;
}