JZOJ573920190706毒奶
Posted paul-guderian
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JZOJ573920190706毒奶相关的知识,希望对你有一定的参考价值。
题目
有\(n\)个现实城市,另有\(n\)个幻想城市
原图中在现实城市存在\(m\)条边,在幻想城市存在\(m-1-n\)条边
一个排列是合法的当且进当显示城市 \(i\) 向幻想城市 \(p_i\) 连边后,图是连通的
求合法的排列数目
\(n \le 20 \ , \ 时限10s\)
题解
- 如果初始连通块个数\(\gt n+1\)个无解;
- 考虑这\(n+1\)个连通块,设\(S\)表示已经分配好的连通块集合的合法排列个数
- 转移时用总的减去不合法的,不合法考虑枚举编号最小的城市所在联通块
- 时间复杂度:\(O(3^20)\)
- 由于一个状态如果不满足现实和幻想的连通块点数和相等就是无用的,可以去掉很多无用状态
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;
以上是关于JZOJ573920190706毒奶的主要内容,如果未能解决你的问题,请参考以下文章