标签:col 顺序 排列 text == return lin 选择 using
全程膜拜 码得都要一样了。。
对于这种数列置换的可以理解成多个环,而对于一个大小为d的环把顺序弄对要做d-1次
总起来就是n-环数的次数 加上暴力30pt到手啦
假如题目没有限制,那就是第一类斯特林数,因为点有序再乘一个所有点的全排列 又20pt到手啦,考场上就溜了
现在考虑分情况讨论。如果我们让第一个数列的数字连向第二个数列的对应数字(或许这样理解环容易些),将有四种情况:上面是否有当前数字,下面是否有当前数字。给出的限制相当于提前先给了一些链
假如用二进制表示,0没有1有会方便点
对于11就不用管了,对于直接就是一个环的更不用管了,最后减一下直接是一个环的个数就好
主要问题在01和10,这两个是类似的,容易发现它们是不能互接成链的(废话要不就成11了),处理一个就行了吧。处理10的情况
我们先把出现10情况的数字个数处理出来
设f[i]表示这些数字构成的环数为i的方案数
当然这样的东西肯定是要上个容斥的,设g[i]是至少为i,要有g[i]=sigema(i~n)j f[j]*j的容斥系数
这样g[x]=sigema(x^k)i C(k,i)*S1(i,x)*(m+k-i)^(k-i)
k为10情况的数字个数,m为00情况的个数,含义为选i个数字,然后把它划分到x个环,剩下的k-i个10情况的数字可以选择接去其中一个状态为00的数字,连了边以后相当于一个10一个01,我们也可以看成一个11一个00,恰好抵消扔给00的情况自己处理,也可以接到一个未划分的或者不接,未划分的自己也会找其他的,不接就自环咯。因为选了就不能再选所以是降次幂
考虑f对g的贡献,对于fd,对gx的贡献为C(d,x),意为d中选x个环给选中,于是g[i]=sigema(i~n)j f[j]*C(j,i),直接二项式反演即可
00的情况就是20pt的做法
as[x]=∑∑∑f(i)g(j)h(k)(i+j+k==x) 分别是10,01,00的情况
做两次卷积,因为数据小那就不彂(fa)發(fa)闧(ta)了我不会NTT啊,直接暴力搞
#include<cstdio> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> #include<cmath> #define rcy(x) ((x)%2==0?1:-1) using namespace std; typedef long long LL; const int _=100; const int maxn=2*1e3+_; const LL mod=998244353; LL C[maxn][maxn],S[maxn][maxn],A[maxn][maxn];//i的降j次幂 void yu() { C[0][0]=S[0][0]=A[0][0]=1; for(int i=1;i<maxn;i++) { C[i][0]=A[i][0]=1; for(int j=1;j<=i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod, S[i][j]=(S[i-1][j-1]+(i-1)*S[i-1][j])%mod, A[i][j]=A[i][j-1]*(i-j+1)%mod; } } //------------------------------------------def------------------------------------------------------- bool vis[maxn*2];int n,nxt[maxn*2],cnt[4];//0-x,x-0,x-x,0-0 void dfs(int x,int st) { vis[x]=true; if(nxt[x]==0) { if(x>n&&st>n)cnt[3]++; else if(x<=n&&st>n)cnt[0]++; else if(x>n&&st<=n)cnt[1]++; } else { if(!vis[nxt[x]])dfs(nxt[x],st); else cnt[2]++; } } //-------------------------------------------分类讨论---------------------------------------------- void calc(LL *u,int k) { for(int x=0;x<=k;x++) for(int i=x;i<=k;i++) u[x]=(u[x]+C[k][i]*S[i][x]%mod*A[cnt[3]+k-i][k-i])%mod; for(int i=0;i<=k;i++) { for(int j=i+1;j<=k;j++) u[i]=(u[i]+rcy(j-i)*u[j]*C[j][i])%mod; u[i]=(u[i]+mod)%mod; } } LL f[maxn],g[maxn],h[maxn],as[maxn]; void solve() { calc(f,cnt[0]),calc(g,cnt[1]); for(int i=0;i<=n;i++) for(int j=0;j<=i;j++) h[i]=(h[i]+f[j]*g[i-j])%mod; for(int i=0;i<=n;i++) for(int j=0;j<=i;j++) as[i]=(as[i]+h[j]*S[cnt[3]][i-j])%mod; for(int i=0;i<=n;i++)as[i]=as[i]*A[cnt[3]][cnt[3]]%mod; } //--------------------------------------------solve------------------------------------------------- int a[maxn],b[maxn]; bool ru[maxn*2]; int main() { freopen("gift.in","r",stdin); freopen("gift.out","w",stdout); yu(); scanf("%d",&n); for(int i=1;i<=n;i++)scanf("%d",&a[i]); for(int i=1;i<=n;i++)scanf("%d",&b[i]); memset(vis,true,sizeof(vis)); for(int i=1;i<=n;i++) { if(a[i]==0)a[i]=i+n; if(b[i]==0)b[i]=i+n; vis[a[i]]=vis[b[i]]=false; if(a[i]<=n||b[i]<=n) nxt[a[i]]=b[i],ru[b[i]]=true; } for(int i=1;i<=2*n;i++) if(!vis[i]&&!ru[i])dfs(i,i); for(int i=1;i<=2*n;i++) if(!vis[i])dfs(i,i); solve(); for(int i=0;i<n;i++) printf("%lld ",n-cnt[2]-i>=0?as[n-cnt[2]-i]:0); puts(""); return 0; }
标签:col 顺序 排列 text == return lin 选择 using
原文地址:https://www.cnblogs.com/AKCqhzdy/p/10336882.html