其他分享
首页 > 其他分享> > 【JZOJ5739】【20190706】毒奶

【JZOJ5739】【20190706】毒奶

作者:互联网

题目

有\(n\)个现实城市,另有\(n\)个幻想城市

原图中在现实城市存在\(m\)条边,在幻想城市存在\(m-1-n\)条边

一个排列是合法的当且进当显示城市 \(i\) 向幻想城市 \(p_i\) 连边后,图是连通的

求合法的排列数目

\(n \le 20 \ , \ 时限10s\)

题解

Code

#include<bits/stdc++.h>
#define mod 998244353
using namespace std;
const int N=21;
int n,m,n1,n2,tot,f[N],sz[N],sz1[1<<N],sz2[1<<N],all1,all2,all,fac[N],F[1<<N],G[1<<N];
int find(int x){return f[x]==x?x:f[x]=find(f[x]);}
void dec(int&x,int y){x-=y;if(x<0)x+=mod;}
void uni(int u,int v){
    int fu=find(u),fv=find(v);
    if(fu==fv)return;
    f[fu]=fv;sz[fv]+=sz[fu];
}
int main(){
    freopen("milk.in","r",stdin);
    freopen("milk.out","w",stdout);
    
    scanf("%d%d",&n,&m);
    for(int i=fac[0]=1;i<=n;++i)fac[i]=1ll*fac[i-1]*i%mod;
    
    for(int i=1;i<=n;++i)f[i]=i,sz[i]=1;
    for(int i=1,u,v;i<=m;++i){
        scanf("%d%d",&u,&v);
        uni(u,v);
    }
    for(int i=1;i<=n;++i)if(find(i)==i){
        sz1[1<<tot++]=sz[i];
    }
    n1=tot;tot=0;all1=(1<<n1)-1;
    for(int i=0;i<n1;++i)
    for(int j=0;j<=all1;++j)
        if(j>>i&1)sz1[j]+=sz1[j^(1<<i)];
    
    m=n-1-m;
    for(int i=1;i<=n;++i)f[i]=i,sz[i]=1;
    for(int i=1,u,v;i<=m;++i){
        scanf("%d%d",&u,&v);
        uni(u,v);
    }
    for(int i=1;i<=n;++i)if(find(i)==i){
        sz2[1<<tot++]=sz[i];
    }
    n2=tot;tot=0;all2=(1<<n2)-1;
    for(int i=0;i<n2;++i)
    for(int j=0;j<=all2;++j)
        if(j>>i&1)sz2[j]+=sz2[j^(1<<i)];
    
    all=(1<<(n1+n2))-1;
    if(n1+n2!=n+1){puts("0");return 0;}
    F[0]=G[0]=1;
    for(int S=1;S<=all;++S){
        int v1=sz1[S&all1],v2=sz2[S>>n1];
        if(v1!=v2)continue;
        G[S]=fac[v1];
    }
    for(int S=1;S<=all;++S)if(G[S]){
        F[S]=G[S];
        int x=S&-S,R=S^x;
        if(x>all1)continue;
        for(int T=R;T;T=(T-1)&R)if(G[T]){
            dec(F[S],1ll*G[T]*F[S^T]%mod);
        }
    }
    cout<<F[all]<<endl;
    return 0;
}

标签:幻想,20190706,毒奶,int,城市,连通,合法,sz1,JZOJ5739
来源: https://www.cnblogs.com/Paul-Guderian/p/11146712.html