题意:
给出一个长度为
n
(
≤
1
e
5
)
n(\leq 1e5)
n(≤1e5)的排列,其中有
K
(
≤
12
)
K(\leq 12)
K(≤12)个位置不确定,在填好这些不确定的位置后得到的所有排列,若干次选很多对位置将每对位置上的两个数交换位置,求所有排列排成升序所需的最少操作次数之和,在最少操作次数的情况下的操作方案数之和。
结论题。
1.排列可以看做置换,置换可以看做若干个环,环的大小均
≤
2
\leq 2
≤2时只需要一步,方案只有一种,否则也只需要两步。
2.对于一个长度为
n
n
n的环,不难发现,在第一步后的第一位填
[
1
,
n
]
[1, n]
[1,n] 中任意一个值之后,第一步后的剩余位置均可直接推出,且不会产生矛盾。这样第一步后的第一位的值就对应一种方案,即
共有
n
n
n 种方案。
3.对于两个长度为
n
n
n的环,考虑跨环的交换会发生什么,在第一步后第一位填另一个环内的任意一个值之后,其余位置又可以直接推出且不出现矛盾。这样就又出现了 n 种方案。
4.根据之前的分析可以知道,一个环要么单独排序,方案数为 n;要么和另一个长度相同的环一起排序,方案数也为 n。
5.长度不同的环之间如果跨环交换,则无法在两步之内排好序。于是每一种长度的环是独立的,将每一种长度的方案数相乘即可。
6.k > 0 时,原排列可以看成若干个环加上 k 条路径,补全排列实际上是将这 k条路径组合成环。
直接枚举排列复杂度过高,由于方案数只与环的大小有关,而与路径组合成环的顺序无关,考虑枚举 k 条路径的每一种划分。预处理出 dp(i, j), i ∈ [1, n], j ∈ [0, n/i],并计算 dp(i, j) 的逆元。开个数组记
录原排列中每种长度的环的个数,并计算仅考虑这些环时的方案数。枚举完一个划分后只会最多增加 k 个环,可以在 O(k) 内算出新的方案数。
总复杂度为 O(n log n + bell(k) × k)。
这波,这波是时间管理。
A
C
C
o
d
e
\rm AC\ Code
AC Code
#include<bits/stdc++.h>
#define maxn 100005
#define mod 1000000007
using namespace std;
int Pow(int b,int k){ int r=1;for(;k;k>>=1,b=1ll*b*b%mod) if(k&1) r=1ll*r*b%mod; return r; }
int n,K;
int *f[maxn],*fr[maxn],a[maxn],b[maxn];
bool usd[maxn];
int ar[maxn],tot,c[maxn];
int sg,sf,sm=1,ct=0,csm[1<<12],lg[1<<12],bit[1<<12],fac[13]={1};
void dfs(int sta,int coe){
if(!sta){
if(ct == 0){
if(c[2])
sf += coe , sg+= coe;
else
sg += coe;
}
else sf += 2 * coe , sg = (sg + 1ll * coe * sm) % mod;
return;
}
int t = sta & -sta;
for(int s=sta;s;s=(s-1)&sta) if(s & t){
int p = csm[s];
if(p > 2) ct ++;
sm = 1ll * sm * fr[p][c[p]] % mod * f[p][c[p]+1] % mod;
c[p]++;
dfs(sta-s,coe * 1ll * fac[bit[s]-1] % mod);
sm = 1ll * sm * fr[p][c[p]] % mod * f[p][c[p]-1] % mod;
c[p]--;
if(csm[s] > 2) ct --;
}
}
int main(){
freopen("sort.in","r",stdin);
freopen("sort.out","w",stdout);
scanf("%d%d",&n,&K);
for(int i=1;i<=n;i++){
f[i] = new int[n / i + 2];
fr[i]= new int[n / i + 2];
f[i][0] = 1 , f[i][1] = i;
fr[i][0] = 1 , fr[i][1] = Pow(i,mod-2);
for(int j=2;j<=n/i;j++)
f[i][j] = (f[i][j-1] * 1ll * i+ 1ll * i * f[i][j-2] % mod * (j-1)) % mod,
fr[i][j] = Pow(f[i][j],mod-2);
}
for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[a[i]]=i;
for(int i=1;i<=n;i++) if(!a[i]){
int cnt = 1;
usd[i]=1;
for(int j=b[i];j;j=b[j]) usd[j]=1,cnt++;
ar[tot++] = cnt;
}
for(int i=0;i<K;i++) lg[1<<i] = i;
for(int i=1;i<=K;i++) fac[i] = 1ll * fac[i-1] * i % mod;
for(int i=1;i<(1<<K);i++) bit[i] = bit[i>>1] + (i&1);
for(int i=1;i<=n;i++) if(!usd[i]){
int cnt = 0;
for(int j=i;!usd[j];j=a[j])
usd[j] = 1 , cnt++;
c[cnt]++;
}
for(int i=1;i<=n;i++) if(c[i]){
sm = 1ll * sm * f[i][c[i]] % mod;
if(i > 2) ct += c[i];
}
for(int i=1;i<(1<<K);i++)
csm[i] = csm[i - (i&-i)] + ar[lg[i&-i]];
dfs((1<<K)-1,1);
printf("%d\n%d\n",sf,sg);
}