[DP][容斥原理][斯特林数] Codeforces715 E - Complete the Permutations

Solution S o l u t i o n

dfs dfs 出四个量 c1,c2,cyc,blk c 1 , c 2 , c y c , b l k 表示缩边以后 x 0 x →   0 0x 0 → x ,自环和 00 0 → 0 的边的数量。
fi f i 表示用 c1 c 1 可以搞出至少 i i 个环的方案数。直接枚举放几个进去,剩下的和00的搞在一起。
然后容斥一下得到恰好 i i 个环的方案数。
c2同理。
00 0 → 0 放到 i i 个环就是s(blk,i)
把这三个方案数卷起来。
因为 00 0 → 0 的是本质不同的。最后把答案乘上 blk! b l k !
复杂度是 O(n2) O ( n 2 ) ,可以做更大的数据。

#include <bits/stdc++.h>
#define show(x) cerr << #x << " = " << x << endl
using namespace std;

typedef long long ll;
typedef pair<int, int> pairs;
const int N = 555;
const int MOD = 998244353;

inline char get(void) {
    static char buf[100000], *S = buf, *T = buf;
    if (S == T) {
        T = (S = buf) + fread(buf, 1, 100000, stdin);
        if (S == T) return EOF;
    }
    return *S++;
}
template<typename T>
inline void read(T &x) {
    static char c; x = 0; int sgn = 0;
    for (c = get(); c < '0' || c > '9'; c = get()) if (c == '-') sgn = 1;
    for (; c >= '0' && c <= '9'; c = get()) x = x * 10 + c - '0';
    if (sgn) x = -x;
}

int s[N][N], C[N][N], A[N][N];
int n, cyc, blk, c1, c2;
int vis[N], nxt[N], deg[N];
int f[N], h[N], g[N], ans[N];
int a[N], b[N];
queue<pairs> Q;

int sum(int x, int y) {
    return (x + y >= MOD) ? x + y - MOD : x + y;
}
int sub(int x, int y) {
    return (x < y) ? x - y + MOD : x - y;
}
void pre(int n) {
    s[0][0] = C[0][0] = A[0][0] = 1;
    for (int i = 1; i <= n; i++) {
        C[i][0] = A[i][0] = 1; s[i][0] = 0;
        for (int j = 1; j <= i; j++) {
            A[i][j] = sum((ll)j * A[i - 1][j - 1] % MOD, A[i - 1][j]);
            C[i][j] = sum(C[i - 1][j - 1], C[i - 1][j]);
            s[i][j] = sum(s[i - 1][j - 1], (ll)(i - 1) * s[i - 1][j] % MOD);
        }
    }
}
inline void dfs(int u, int cur, int head) {
    vis[u] = 1;
    if (nxt[u]) {
        if (!vis[nxt[u]]) dfs(nxt[u], cur ^ 1, head);
        else ++cyc;
    } else {
        if (head == cur && cur == 1) ++blk;
        if (head == 1 && cur == 0) ++c1;
        if (head == 0 && cur == 1) ++c2;
    }
}
void calc(int n, int *f) {
    for (int i = 0; i <= n; i++) f[i] = 0;
    for (int i = n; i >= 0; i--) {
        for (int j = 0; j <= n; j++)
            f[i] = sum(f[i], (ll)C[n][j] * s[j][i] % MOD * A[n - j + blk][n - j] % MOD);
        for (int j = i + 1; j <= n; j++)
            f[i] = sub(f[i], (ll)f[j] * C[j][i] % MOD);
    }
}

int main(void) {
    freopen("1.in", "r", stdin);
    freopen("1.out", "w", stdout);
    read(n); pre(n);
    for (int i = 1; i <= n; i++) read(a[i]);
    for (int i = 1; i <= n; i++) read(b[i]);
    for (int i = 1; i <= n; i++) {
        if (a[i]) {
            nxt[a[i] + n] = i; ++deg[i];
        }
        if (b[i]) {
            nxt[i] = b[i] + n; ++deg[b[i] + n];
        }
    }
    for (int i = 1; i <= n + n; i++)
        if (!vis[i] && !deg[i])
            dfs(i, i > n, i > n);
    for (int i = 1; i <= n + n; i++)
        if (!vis[i])
            dfs(i, i > n, i > n);
    calc(c1, f); calc(c2, g);
    for (int i = 0; i <= n; i++)
        for (int j = 0; j <= i; j++)
            h[i] = sum(h[i], (ll)f[j] * g[i - j] % MOD);
    for (int i = 0; i <= n; i++) {
        for (int j = 0; j <= i; j++)
            ans[i] = sum(ans[i], (ll)h[j] * s[blk][i - j] % MOD);
        ans[i] = (ll)ans[i] * A[blk][blk] % MOD;
    }
    for (int i = 0; i < n; i++)
        if (n - i - cyc < 0) printf("0\n");
        else printf("%d ", ans[n - i - cyc]);
    putchar('\n');
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值