[ABC214G]Three Permutations
Posted Tan_tan_tann
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[ABC214G]Three Permutations相关的知识,希望对你有一定的参考价值。
Three Permutations
题解
很明显,对于排列,
p
,
q
p,q
p,q,我们可以先将
p
p
p排成一个有序的排列,再将
q
q
q置换成与
q
q
q对应的样子,这并不影响我们的答案,相当于将我们的答案序列置换一下。
此时,对于第
i
i
i个位置,它不能取
i
i
i与
q
i
q_{i}
qi这两个值。考虑一下如果我们现在将值的特殊性去掉,它会变成什么样子。
我们将点
i
i
i与点
q
i
q_{i}
qi之间连一条有向边,我们最后得到的就是许多个环,如果这样构成的图是等价的,那么两个序列就是等价的。
我们对于同一个环上的点的要求就是它不能选择它自身的值与它连边到达的点的值。
即使转化成了图的形式,直接解出答案依旧是十分困难的,我们可以考虑容斥。
容斥自然容斥的是有多少个点选择它自身的值或它这条有向边到达的点的值。
点的选择对于不同的环之间是独立的,我们先单独考虑一个环。
对于一个环上的点,我们显然可以用
d
p
dp
dp求出,设
d
p
i
,
j
,
0
/
1
/
2
,
0
/
1
/
2
dp_{i,j,0/1/2,0/1/2}
dpi,j,0/1/2,0/1/2表示一条长度为
i
i
i的链,有
j
j
j个不合法的选择,链首选择的与它自身/与它相连的点/其他选择的,链尾选择的与它自身/与它相连的点/其他选择的情况数,转移到环直接首尾相连看是否合法即可。
唯一不行的转移就是从
1
1
1转移到
0
0
0,这种情况会让同一个数被选两次,其他情况都是合法的,可以相互转移。
求出一个环上的方案数,环与环之间直接多项式乘法乘起来即可。
设整个图中选择
i
i
i个点不合法的方案数为
g
i
g_{i}
gi,容易得出答案:
A
n
s
=
n
!
+
∑
i
=
1
n
(
−
1
)
i
(
n
−
i
)
!
g
i
Ans=n!+\\sum_{i=1}^{n}(-1)^i(n-i)!g_{i}
Ans=n!+i=1∑n(−1)i(n−i)!gi
(
n
−
i
)
!
(n-i)!
(n−i)!也就是除了那些不合法的点,其他点的选择情况,直接暴力容斥一下就行了。
时间复杂度 O ( n 2 ) O\\left(n^2\\right) O(n2)。
源码
#include<cstdio>
#include<cmath>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;
#define MAXN 3005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long LL;
typedef unsigned long long uLL;
const LL INF=1000000000000000000LL;
const int mo=1e9+7;
const int inv2=499122177;
const int jzm=2333;
const int lim=100000;
const int orG=3,invG=332748118;
const double Pi=acos(-1.0);
const double eps=1e-7;
typedef pair<int,int> pii;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
_T f=1;x=0;char s=getchar();
while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
LL gcd(LL a,LL b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1LL)t=1ll*a*t%p;a=1ll*a*a%p;s>>=1LL;}return t;}
int n,p[MAXN],q[MAXN],a[MAXN],dp[MAXN][MAXN][3][3],sum[MAXN][MAXN],num;
int pow2[MAXN],fac[MAXN],inv[MAXN],f[MAXN],g[MAXN],ans,tmp[MAXN];
bool vis[MAXN];
void dosaka(int u){num++;vis[u]=1;if(!vis[a[u]])dosaka(a[u]);}
void init(){
pow2[0]=fac[0]=fac[1]=inv[0]=inv[1]=f[1]=1;pow2[1]=2;
for(int i=2;i<=n;i++)
fac[i]=1ll*i*fac[i-1]%mo,
f[i]=1ll*(mo-mo/i)*f[mo%i]%mo,
inv[i]=1ll*f[i]*inv[i-1]%mo,
pow2[i]=add(pow2[i-1],pow2[i-1],mo);
}
signed main(){
read(n);init();
for(int i=1;i<=n;i++)read(p[i]);
for(int i=1;i<=n;i++)read(q[i]);
dp[1][1][0][0]=dp[1][1][1][1]=dp[1][0][2][2]=sum[1][0]=sum[1][1]=1;
for(int i=2;i<=n;i++)
for(int j=0;j<=i;j++){
for(int k=0;k<3;k++){
if(j)dp[i][j][k][0]=add(dp[i-1][j-1][k][0],dp[i-1][j-1][k][2],mo);
if(j)dp[i][j][k][1]=add(add(dp[i-1][j-1][k][0],dp[i-1][j-1][k][1],mo),dp[i-1][j-1][k][2],mo);
dp[i][j][k][2]=add(add(dp[i-1][j][k][0],dp[i-1][j][k][1],mo),dp[i-1][j][k][2],mo);
}
for(int k=0;k<3;k++)for(int l=0;l<3;l++)
if(k!=0||l!=1)sum[i][j]=add(sum[i][j],dp[i][j][k][l],mo);
}
for(int i=1;i<=n;i++)a[p[i]]=q[i];g[0]=1;int summ=0;
for(int i=1;i<=n;i++)if(!vis[i]){
num=0;dosaka(i);summ+=num;
for(int k=0;k<=summ-num;k++)
for(int j=0;j<=num;j++)
tmp[k+j]=add(tmp[k+j],1ll*sum[num][j]*g[k]%mo,mo);
for(int j=1;j<=summ;j++)g[j]=tmp[j],tmp[j]=0;
}
ans=fac[n];
for(int i=1;i<=n;i++)
if(i&1)ans=add(ans,mo-1ll*fac[n-i]*g[i]%mo,mo);
else ans=add(ans,1ll*fac[n-i]*g[i]%mo,mo);
printf("%d\\n",ans);
return 0;
}
谢谢!!!
以上是关于[ABC214G]Three Permutations的主要内容,如果未能解决你的问题,请参考以下文章
Codeforces Round #615 (Div. 3). C - Product of Three Numbers
Lintcode15 Permutations solution 题解