Loading

统一省选 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;
}
posted @ 2023-09-04 20:56  Chy12321  阅读(20)  评论(0编辑  收藏  举报