【CF715E】Complete the Permutations【bzoj5406】Gift

Source

bzoj5406
CF715E

Hint

请先思考后再展开

看到排列的置换问题,套路地转化为图
上连下,并把非0的相同数字合并
那么就会形成一些环和链,如果没有0,代价就是n-环数

Solution

请先思考后再展开

对于每条链,我们只关心两端是什么类型的(1表示非0),例如没有成环的11就是无用的
01、10与00合并,意味着决定把那个数字填上去,于是剩下的就是那两个0,也就是00
所以01和10不会直接组合,但可能01与00合并为00后与10组合

那么分开考虑01和10,以10为例:
$$
f_i=\sum_{j=i}^n C_n^j S_1(j,i) (n-j+k)^{\underline{n-j}},k条00,n条10,至少i个环
$$
意思就是选j条边先强制生成i个环,然后剩下的边选择后继,如果选择自己意味着单独作为自环
$$
设g_i为恰好i个环,f_i=\sum_{j=i}^n C_j^i g_j(考虑计算了多少个),二项式反演得 g_i=\sum_{j=i}^n (-1)^{j-i} C_j^i f_j
$$
然后考虑剩下的00,依然有k个,在排列中可以交换顺序,方案为 $T_i=S_1(k,i) \times k!​$

然后把3种类型卷积在一起即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
//Zory-2019
#include<cmath>
#include<ctime>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<map>
#include<set>
#include<queue>
#include<deque>
#include<stack>
#include<bitset>
#include<vector>
#include<algorithm>
#include<iostream>
#include<deque>
using namespace std;
namespace mine
{
typedef long long ll;
const int INF=0x3f3f3f3f;
int qread()
{
int ans=0;char c=getchar();int f=1;
while(c<'0' or c>'9') {if(c=='-') f=-1;c=getchar();}
while('0'<=c and c<='9') ans=ans*10+c-'0',c=getchar();
return ans*f;
}
void write(int num)
{
if(num<0) {num=-num;putchar('-');}
if(num>9) write(num/10);
putchar('0'+num%10);
}
void writeln(int num){write(num);puts("");}
#define PB push_back
#define pr pair<int,int>
#define FR first
#define SE second
#define MP make_pair
inline void chmax(int &x,int y) {x=x>y?x:y;}
inline void chmin(int &x,int y) {x=x<y?x:y;}
const int MAX_N=2100;
bool vis[MAX_N*2];int n,cnt=0,s0=0,s1=0,s2=0;
int a[MAX_N],b[MAX_N],nxt[MAX_N*2],ru[MAX_N*2];
void dfs(int x,int tp)
{
vis[x]=1;
if(nxt[x]>0)
{
if(vis[nxt[x]]) cnt++;
else dfs(nxt[x],tp);
}
else
{
if(tp>n and x<=n) s1++;
if(tp<=n and x>n) s2++;
if(tp>n and x>n) s0++;
}
}
const int MOD=998244353;
void add(ll &x,ll y){x+=y;if(x>=MOD) x-=MOD;if(x<=-MOD) x+=MOD;}
ll sum(ll x,ll y){add(x,y);return x;}
ll g[MAX_N],c[MAX_N][MAX_N],S1[MAX_N][MAX_N],D[MAX_N][MAX_N];
void pre()
{
c[0][0]=1;S1[0][0]=1;D[0][0]=1;
for(int i=1;i<MAX_N;i++)
{
c[i][0]=1;S1[i][0]=0;D[i][0]=1;
for(int j=1;j<=i;j++)
{
c[i][j]=sum(c[i-1][j-1],c[i-1][j]);
S1[i][j]=sum(S1[i-1][j-1],S1[i-1][j]*(i-1)%MOD);
D[i][j]=D[i][j-1]*(i-j+1)%MOD;
}
}
}
void getA(ll f[],int n,int k)
{
memset(g,0,sizeof g);
for(int i=0;i<=n;i++) for(int j=i;j<=n;j++) add(g[i],c[n][j]*S1[j][i]%MOD*D[n-j+k][n-j]%MOD);
for(int i=0;i<=n;i++) for(int j=i,t=1;j<=n;j++,t=-t) add(f[i],c[j][i]*t*g[j]%MOD);
}
void cheng(ll a[],ll b[],int n)
{
memset(g,0,sizeof g);
for(int i=0;i<=n;i++) for(int j=0;j<=n;j++) if(i+j<=n) add(g[i+j],a[i]*b[j]%MOD);
memcpy(a,g,sizeof g);
}
ll A[MAX_N],B[MAX_N],C[MAX_N];
void main()
{
pre();
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]);
for(int i=1;i<=n*2;i++) vis[i]=1;
for(int i=1;i<=n;i++)
if(a[i]+b[i]==0) s0++;
else
{
if(!a[i]) a[i]=i+n;if(!b[i]) b[i]=i+n;
vis[a[i]]=vis[b[i]]=0;
nxt[a[i]]=b[i];ru[b[i]]++;
}
for(int i=1;i<=2*n;i++) if(ru[i]==0 and !vis[i]) dfs(i,i);//先找链
for(int i=1;i<=2*n;i++) if(!vis[i]) dfs(i,i);
getA(A,s1,s0);getA(B,s2,s0);for(int k=0;k<=s0;k++) C[k]=S1[s0][k]*D[s0][s0]%MOD;
cheng(A,B,n);cheng(A,C,n);
for(int i=0;i<=n-1;i++) printf("%lld ",(n-i>=cnt?(A[n-i-cnt]+MOD)%MOD:0) );
}
};
int main()
{
srand(time(0));
mine::main();
}

本文基于 知识共享署名-相同方式共享 4.0 国际许可协议发布
本文地址:http://zory.ink/posts/a5e5.html
转载请注明出处,谢谢!