【CF715E】Complete the Permutations(容斥,第一類斯特林數)
題面
CF
洛谷
給定兩個排列\(p,q\),但是其中有些位置未知,用\(0\)表示。
現在讓你補全兩個排列,定義兩個排列\(p,q\)之間的距離為每次選擇\(p\)中兩個元素交換,使其變成\(q\)的最小次數。
求距離恰好為\([0,n-1]\)的填數方案數。
加強的題目在\(BZOJ\)上有,戳這里。
題解
看到這道題目就覺得無比熟悉。回頭翻了翻發現果然是省隊集訓的時候的題目。。。
果然都是原題啊。。。
如果排列已知,發現交換的最小次數顯然就是沿着置換交換,交換次數為\(n-\)置換個數。那么考慮從\(p_i\)連邊連向\(q_i\),那么要求的就是環的個數。
顯然成鏈的中間的\(x-x\)邊全部可以直接丟掉,那么只需要考慮最終的開頭和結尾就知道這條鏈到底是什么類型的了。
那么邊有四種:已知-已知,已知-\(0\),\(0\)-已知,\(0\)-\(0\)。
對於已經成環的部分,我們顯然不需要再考慮的。那么我們要做的就是把已經存在的鏈給合並成環,還要憑空用\(0-0\)邊構造出一些環。
設有\(k\)條鏈,\(x-0\)和\(0-x\)是分開考慮的,設\(g_i\)表示至少有\(i\)個環。
其中\(m\)是\(0-0\)邊的數量。
這里拿\(x-0\)邊舉例。首先選擇若干個\(x-0\)邊出來拼成\(x\)個環,枚舉使用的邊的個數組合數計算方案,然后把他們拼成環,環排列個數即第一類斯特林數。多出來的邊隨意拼接,顯然他們需要找一個后繼,即把\(0\)位置和另外一條邊給拼起來,無論是選擇另外一個\(0-0\)邊來拼或者選擇一個\(x-0\)邊來拼都是可行的,因為每條邊也只會有一個前驅,所以選擇方案數是下降冪。\(0-x\)邊是同理的。
設\(f_x\)為\(0-x\)邊計算出來的恰好的結果,\(g_x\)為\(x-0\)邊計算出來的恰好的結果。兩者卷積后,得到的是恰好形成了\(x\)環的結果。但是此時還多出了一些\(0-0\)鏈,它們形成環的方案數還是斯特林數。因此再將卷積的結果和斯特林數卷積。
最終因為\(0-0\)之間無順序關系,所以還要乘上一個階乘。
#include<iostream>
#include<cstdio>
using namespace std;
#define MAX 550
#define MOD 998244353
void add(int &x,int y){x+=y;if(x>=MOD)x-=MOD;}
inline int read()
{
int x=0;bool t=false;char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=true,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return t?-x:x;
}
int n,a[MAX],b[MAX],deg[MAX];
int f[MAX],g[MAX],h[MAX],ans[MAX];
int nt[MAX],cnt[4];
bool vis[MAX<<1];
int C[MAX][MAX],A[MAX][MAX],S[MAX][MAX];
void dfs(int u,int ff)
{
vis[u]=true;
if(nt[u])
{
if(!vis[nt[u]])dfs(nt[u],ff);
else cnt[2]+=1;
}
else
{
if(u>n&&ff>n)cnt[3]+=1;
else if(u<=n&&ff>n)cnt[0]+=1;
else if(u>n&&ff<=n)cnt[1]+=1;
}
}
void calc(int *f,int n)
{
for(int i=0;i<=n;++i)
for(int j=i;j<=n;++j)
add(f[i],1ll*C[n][j]*S[j][i]%MOD*A[n-j+cnt[3]][n-j]%MOD);
for(int i=0;i<=n;++i)
{
int t=0;
for(int j=i,d=1;j<=n;++j,d=MOD-d)
add(t,1ll*d*f[j]%MOD*C[j][i]%MOD);
f[i]=t;
}
}
int main()
{
n=read();
for(int i=1;i<=n;++i)a[i]=read();
for(int i=1;i<=n;++i)b[i]=read();
for(int i=1;i<=n+n;++i)vis[i]=true;
for(int i=1;i<=n;++i)
{
if(!a[i])a[i]=i+n;if(!b[i])b[i]=i+n;
vis[a[i]]=vis[b[i]]=false;
if(a[i]<=n||b[i]<=n)nt[a[i]]=b[i],++deg[b[i]];
}
for(int i=1;i<=n+n;++i)if(!vis[i]&&!deg[i])dfs(i,i);
for(int i=1;i<=n+n;++i)if(!vis[i])dfs(i,i);
C[0][0]=S[0][0]=A[0][0]=1;
for(int i=1;i<=n;++i)
{
A[i][0]=C[i][0]=1;
for(int j=1;j<=i;++j)
{
C[i][j]=(C[i-1][j]+C[i-1][j-1])%MOD;
A[i][j]=1ll*A[i][j-1]*(i-j+1)%MOD;
S[i][j]=(S[i-1][j-1]+1ll*(i-1)*S[i-1][j])%MOD;
}
}
calc(f,cnt[0]);calc(g,cnt[1]);
for(int i=0;i<=n;++i)
for(int j=0;j<=i;++j)
add(h[i],1ll*f[j]*g[i-j]%MOD);
for(int i=0;i<=n;++i)
for(int j=0;j<=i;++j)
add(ans[i],1ll*h[j]*S[cnt[3]][i-j]%MOD);
for(int i=0;i<=n;++i)ans[i]=1ll*ans[i]*A[cnt[3]][cnt[3]]%MOD;
for(int i=0;i<n;++i)printf("%d ",n-i-cnt[2]>=0?ans[n-i-cnt[2]]:0);
puts("");return 0;
}
