loj2540. 「PKUWC2018」随机算法

题意

略。

题解

听说考场上暴力搜出独立集有90分
这道题的状态还是挺难找的。
初始排列为空。考虑设\(f_{s, i}\)表示当前状态,独立集为\(s\),已经不在独立集里面(即与\(s\)中的点有连边)且还没有加入排列的点数为\(i\)
则有初始状态\(f_{0, 0} = 1\)
考虑转移,如果某一个点可以加入这个独立集,则:
\[ f_{s \cup \{x\}, i + \text{new}(x, s)} += f_{s, i} \ (x \notin s) \]
其中\(new(x, s)\)代表的是\(x\)有连边,并且不属于\(s\)且不与\(s\)中任何点有连边的点的个数。
这个操作代表将\(x\)加入排列。
只有这样一种操作是不够的,考虑要把已经不在独立集里面且还没有加入排列的点加入排列,如果有\(i\)个这样的点,那么这次可以选择任何一个加入。

\[ f_{s, i - 1} += f_{s, i} \]
考虑到转移一定构成了一个DAG(先按集合\(s\)的偏序,再按点数\(i\)的偏序),所以是没问题的。
但是转移的时候要注意后一种操作是可以不断地做的,所以\(i\)的枚举方向是从\(n\)\(1\)
复杂度\(\mathcal O(2 ^ n n ^ 2)\)

#include 
#define fi first
#define se second
using namespace std;
typedef long long ll;

const int N = 20, M = 1 << 20, mod = 998244353;
int n, m, c, ans, e[M], d[N][M], t[M], f[N][M];
int power (int x, int y) {
    int ret = 1;
    for ( ; y; y >>= 1, x = 1ll * x * x % mod) {
        if (y & 1) {
            ret = 1ll * ret * x % mod;
        }
    }
    return ret;
}
void U (int &x, int y) {
    if ((x += y) >= mod) {
        x -= mod;
    }
}
int main () {
    cin >> n >> m;
    for (int i = 0; i < n; ++i) {
        e[1 << i] = 1 << i;
    }
    for (int i = 1, x, y; i <= m; ++i) {
        cin >> x >> y, --x, --y;
        e[1 << x] |= 1 << y, e[1 << y] |= 1 << x;
    }
    m = 1 << n;
    for (int i = 0; i < n; ++i) {
        for (int s = 0; s < m; ++s) {
            if (s >> i & 1) {
                e[s] |= e[s ^ 1 << i];
            }
        }
    }
    for (int i = 0; i < n; ++i) {
        for (int s = 0; s < m; ++s) {
            if (~e[s] >> i & 1) {
                d[i][s] = __builtin_popcount(e[s] | e[1 << i] ^ (1 << i)) - __builtin_popcount(e[s]);
            }
        }
    }
    f[0][0] = 1;
    for (int s = 0; s < m; ++s) {
        for (int i = n; i; --i) {
            U(f[i - 1][s], 1ll * f[i][s] * i % mod);
        }
        for (int i = n; ~i; --i) {
            if (f[i][s]) {
                for (int x = 0; x < n; ++x) {
                    if (~e[s] >> x & 1) {
                        U(f[i + d[x][s]][s | 1 << x], f[i][s]);
                    }
                }
            }
        }
    }
    for (int s = 0; s < m; ++s) {
        if (f[0][s]) {
            if (__builtin_popcount(s) > c) {
                c = __builtin_popcount(s), ans = 0;
            }
            if (__builtin_popcount(s) == c) {
                U(ans, f[0][s]);
            }
        }
    }
    for (int i = 1; i <= n; ++i) {
        ans = 1ll * ans * power(i, mod - 2) % mod;
    }
    cout << ans << endl;
    return 0;
}

你可能感兴趣的:(loj2540. 「PKUWC2018」随机算法)