ICPC2021 上海 Strange Permutations 和 ICPC2021 沈阳 Perfect Matchings

Strange Permutations

Given a permutation \(P\) of \(\{1,2,\cdots, n\}\), determine the number of \(\{1,2,\cdots, n\}\) permutations \(Q\) satisfying that \(\forall i \in \{1, 2, \cdots, n - 1\}, Q_{i+1} \neq P_{Q_i}\). Output the number modulo \(998244353\).

\(1\le n\le 10^5\)

题解

首先这个\(Q_{i+1} \neq P_{Q_i}\)限制很奇怪,不好理解。不如把\(P\)看成一个函数,然后就题目变成了有\(n\)个限制,排列中不能连续地出现\(x, P(x)\)

容斥是显然的。钦定一定违反了\(k\)个限制,然后有一些数前后黏在一起,带上容斥系数\((-1)^k\)

但是我们可以发现这些限制形成了一些环,容斥的时候一个环内的限制不可能同时违反。若环长为\(l\),一个排列最多违反\(l-1\)个限制。并且环与环之间是互不影响的。

那么对每个环构造容斥系数多项式,乘起来即可。

时间复杂度\(O(n\log^2 n)\)

constexpr int L=17, N=1<<L;
using Poly=vector<int>;
int omg[2][N+1], rev[N+1];
int fac[N+1], inv[N+1], ifac[N+1];

void initNTT(){
    omg[0][0]=1, omg[0][1]=fastPow(3, (MOD-1)/N);
    omg[1][0]=1, omg[1][1]=fastPow(omg[0][1], MOD-2);
    rev[0]=0, rev[1]=1<<(L-1);
    fac[0]=fac[1]=1;
    inv[0]=inv[1]=1;
    ifac[0]=ifac[1]=1;
    for(int i=2; i<=N; ++i){
        omg[0][i]=mul(omg[0][i-1], omg[0][1]);
        omg[1][i]=mul(omg[1][i-1], omg[1][1]);
        rev[i]=rev[i>>1]>>1|(i&1)<<(L-1);
        fac[i]=mul(fac[i-1], i);
        inv[i]=mul(MOD-MOD/i, inv[MOD%i]);
        ifac[i]=mul(ifac[i-1], inv[i]);
    }
}

void NTT(Poly &a, int dir){
    int lim=a.size(), len=log2(lim);
    for(int i=0; i<lim; ++i){
        int r=rev[i]>>(L-len);
        if(i<r) swap(a[i], a[r]);
    }
    for(int i=1; i<lim; i<<=1)
        for(int j=0; j<lim; j+=i<<1)for(int k=0; k<i; ++k){
            int t=mul(omg[dir][N/(i<<1)*k], a[j+i+k]);
            a[j+i+k]=add(a[j+k], MOD-t), a[j+k]=add(a[j+k], t);
        }
    if(dir)for(int i=0; i<lim; ++i)
        a[i]=mul(a[i], inv[lim]);
}

Poly operator*(Poly a, Poly b){
    int n=a.size()+b.size()-1, lim=1<<(int)ceil(log2(n));
    a.resize(lim), NTT(a, 0);
    b.resize(lim), NTT(b, 0);
    for(int i=0; i<lim; ++i) a[i]=mul(a[i], b[i]);
    NTT(a, 1), a.resize(n);
    return a;
}

int p[N], vis[N];
Poly f[N];

int C(int n, int m){
    return mul(fac[n], mul(ifac[m], ifac[n-m]));
}

Poly build(int l, int r){
    if(l==r) return f[l];
    int mid=(l+r)>>1;
    return build(l, mid)*build(mid+1, r);
}

int main(){
    initNTT();
    int n=read<int>();
    for(int i=1; i<=n; ++i) read(p[i]);
    int m=0;
    for(int i=1; i<=n; ++i)if(!vis[i]){
        int len=0;
        function<void(int)> dfs=[&](int x)->void{
            vis[x]=1, ++len;
            if(!vis[p[x]]) dfs(p[x]);
        };
        dfs(i);
        f[++m].resize(len+1);
        for(int j=0; j<=len-1; ++j){
            f[m][len-j]=C(len, j);
            if(j&1) f[m][len-j]=MOD-f[m][len-j];
        }
    }
    // for(int i=1; i<=m; ++i){
    //     cerr<<"f"<<i<<" = ";
    //     for(int a:f[i]) cerr<<a<<" ";
    //     cerr<<endl;
    // }
    Poly g=build(1, m);
    int ans=0;
    for(int i=1; i<=n; ++i) ans=add(ans, mul(g[i], fac[i]));
    printf("%d\n", ans);
    return 0;
}

Perfect Matchings

AAA gets a complete graph of \(2n\) vertices, where every pair of distinct vertices is connected by a unique edge, as a birthday present. However, AAA thinks the complete graph is not that beautiful and he decides to delete \(2n-1\) edges that form a tree.

Now he wonders the number of different perfect matchings in the remaining graph. Note that a perfect matching is a set of \(n\) edges where no two edges share a common vertex. Since the answer may be very large, you only need to output the answer modulo \(998\,244\,353\).

\(2\le n\le 2\,000\)

题解

不删边的时候答案是\((2n-1)!!\)

容斥选了哪些删了的树边即可。需要用到树上背包DP及其优化。

时间复杂度\(O(n^2)\)

constexpr int N=4e3+10;
int fac[N];
vector<int> to[N];
int f[N][N][2], g[N][2], siz[N];

void dfs(int x, int fa){
    f[x][0][0]=1, siz[x]=1;
    for(int y:to[x])if(y!=fa){
        dfs(y, x);
        for(int i=0; i<=(siz[x]+siz[y])/2; ++i)
            g[i][0]=g[i][1]=0;
        for(int i=0; i<=siz[x]/2; ++i)
            for(int j=0; j<=siz[y]/2; ++j){
                g[i+j][0]=add(g[i+j][0], mul(f[x][i][0], add(f[y][j][0], f[y][j][1])));
                g[i+j][1]=add(g[i+j][1], mul(f[x][i][1], add(f[y][j][0], f[y][j][1])));
                g[i+j+1][1]=add(g[i+j+1][1], mul(f[x][i][0], f[y][j][0]));
            }
        siz[x]+=siz[y];
        for(int i=0; i<=siz[x]/2; ++i)
            f[x][i][0]=g[i][0], f[x][i][1]=g[i][1];
    }
}
int main(){
    int n=2*read<int>();
    fac[1]=1;
    for(int i=3; i<n; ++i) fac[i]=mul(fac[i-2], i);
    for(int i=1; i<n; ++i){
        int u=read<int>(), v=read<int>();
        to[u].push_back(v), to[v].push_back(u);
    }
    dfs(1, 0);
    int ans=0;
    for(int i=0; i<=n/2; ++i){
        // cerr<<i<<" matches way="<<add(f[1][i][0], f[1][i][1])<<endl;
        int sum=mul(add(f[1][i][0], f[1][i][1]), 2*i<n?fac[n-2*i-1]:1);
        ans=add(ans, i&1? MOD-sum: sum);
    }
    printf("%d\n", ans);
    return 0;
}

posted on 2022-08-11 13:38  autoint  阅读(91)  评论(0编辑  收藏  举报

导航