CF1408G Clusterization Counting

首先,我们需要给一个连通块找到一个直观的合法判定解。

那么我们必须以一种直观的方式将边按照权值分开,这样才能直观地判定一个合法的组。

一个常见的方式是将边从小到大依次加入进来,那么在任意时刻图上存在的边和不存在的边就恰好被一个权值分开了。

那么我们可以很清晰地发现,一个联通块是合法的,当且仅当在上述流程的某个时刻这个连通块会形成一个团。

于是此时一个很暴力的做法就是预处理出所有合法的连通块,然后状压 (dp),但这样是指数级的,显然不可取。

看似这个问题已经难以优化了,但你会发现上面这个依次加边的模型非常类似于 ( m Kruskal) 重构树,那么这个 (dp) 可不可以在重构树上被优化呢?

那么你会发现上面的这个团只可能是 ( m Kruskal) 重构树上的一颗子树或一个单点,同时这些团也可以在 ( m Kruskal) 的流程中求出。

于是问题就转化为给定一棵树,你需要把这颗树划分成 (k) 个联通块,每个可划分的联通块都是给定的的方案。

不难发现这个东西可以直接树形背包 (O(n ^ 2)) 解决。

#include <bits/stdc++.h>
using namespace std;
#define rep(i, l, r) for (int i = l; i <= r; ++i)
#define Next(i, u) for (int i = h[u]; i; i = e[i].next)
const int N = 3000 + 5;
const int M = 1500 + 5;
const int Mod = 998244353;
struct edge { int v, next;} e[N << 1];
int n, tot, cnt, d[N], h[N], sz[N], fa[N], x[M * M], y[M * M], a[M][M], dp[N][M];
int read() {
    char c; int x = 0, f = 1;
    c = getchar();
    while (c > '9' || c < '0') { if(c == '-') f = -1; c = getchar();}
    while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
    return x * f;
}
int Inc(int a, int b) { return (a += b) >= Mod ? a - Mod : a;}
int Mul(int a, int b) { return 1ll * a * b % Mod;}
int find(int x) { return fa[x] == x ? fa[x] : fa[x] = find(fa[x]);}
void add(int u, int v) { 
    e[++tot].v = v, e[tot].next = h[u], h[u] = tot;
    e[++tot].v = u, e[tot].next = h[v], h[v] = tot;
}
void dfs(int u, int fa) {
    int a = 0, b = 0;
    Next(i, u) {
        int v = e[i].v; if(v == fa) continue;
        dfs(v, u); if(!a) a = v; else b = v;
    }
    rep(i, 1, sz[a]) rep(j, 1, sz[b]) dp[u][i + j] = Inc(dp[u][i + j], Mul(dp[a][i], dp[b][j]));
    if(d[u] == sz[u] * (sz[u] - 1) / 2) dp[u][1] = 1;
}
int main() {
    n = cnt = read();
    rep(i, 1, n) rep(j, 1, n) a[i][j] = read(), x[a[i][j]] = i, y[a[i][j]] = j;
    rep(i, 1, 2 * n) fa[i] = i, sz[i] = (i <= n); 
    rep(i, 1, n * (n - 1) / 2) {
        int Fx = find(x[i]), Fy = find(y[i]);
        if(Fx != Fy) {
            d[++cnt] = d[Fx] + d[Fy] + 1, sz[cnt] = sz[Fx] + sz[Fy];
            fa[Fx] = fa[Fy] = cnt, add(cnt, Fx), add(cnt, Fy);
        }
        else ++d[Fx]; 
    }
    dfs(cnt, 0);
    rep(i, 1, n) printf("%d ", dp[cnt][i]);
    return 0;
}

值得一提的是,当我们的做法与某个算法流程本质相同时,可以尝试在这个算法的基础上对我们的做法进行优化。

GO!
原文地址:https://www.cnblogs.com/Go7338395/p/13835197.html