Description
我们有一个是1到n 的排列的序列a1, a2,… ,an。在一秒钟里,我们可以选择一些不相交的数对(u1,v1),(u2,v2),… (uk,vk)并交换所有a_ui 和a_vi,同时对于所有的i 有1<=ui < vi<=n。数对们不相交当且仅当所有的ui 和vi 不相同。
我们想要尽快地把序列重新排列成递增顺序。给定初始排列,计算完成排序的方法数。两种方法不同当且仅当有一个时间t,此时用于交换的数对集合不相同(数对的顺序不重要)。如果给定的排列已经排好序,那么排序所需时间为零,排序方法唯一。
为了让这个问题更有趣,我们在排列上打k个洞。因此,在a1,a2,…,an 中恰好有k个数没有被确定。对于填充这些洞的所有可能情况,计算方法数,然后输出这些值的和模1000000007(10^9 + 7)。
Input
第一行包含两个整数n(1<=n <=10^5)和k(0<=k<=12)。第二行为排列序列a1,…,an(0<=ai<=n)。如果一个数没有被确定,那么它被标记为0。序列有恰好k个0。对于其他不等于零的ai 来说,它们之间两两不相同。
Output
输出方法数之和模1000000007(10^9 + 7)。
Sample Input
输入1:
5 0
1 5 2 4 3
输入2:
5 2
1 0 2 4 0
Sample Output
输出1:
6
输出2:
7
Data Constraint
对于30% 的数据,n<=10。
对于另外30% 的数据,k = 0。
这题。。无力吐槽了,不知道出出来的意义何在。。锻炼思维??
首先。。我们可以证(猜)明(想),从给出状态转移到目标状态最多只需要两步。(不要问我怎么证明,出题人都不证,真是有毒)
知道这个结论之后就是xjb乱搞了。。
首先我们连边,从当前状态连向目标状态,注意是单向。
然后我们肯定会得到环(除非不用步数),那么我们有两种情况,一种是环相交,一种是环不相交。不相交的话,方案数肯定是其中一个环的大小(又是蜜汁结论)。
所以假设现在没有洞,我们的做法大概可以概括如下:
将排列分解成不相交的环。设有c个环,长度都是l。我们设a[c,l]是完全对长度l的c个环进行排序的方法的数量。那么答案就是这样,这是因为只有相同长度的环相互作用(又是蜜汁结论)。
那么我们可以得出一个求解a的公式:
a[c, l] = l·a[c - 1, l] + l·(c - 1)·a[c - 2, l]
那么答案就应该是所有l上的a乘起来。
可是问题是现在还神TM多出来k,这个我们既可以直接暴力枚举,12!可过,也可以dp,考虑所有可能的方式来分割最多12个路径。这时候我们只需将一些常量乘以每个分区,并将其求和就可以了。这样就可以成功减少复杂度。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define pb push_back
using namespace std;
int n,m;
const int N=1e5+5;
const int mo=1e9+7;
typedef long long ll;
int k,p[N],bz[N],vis[N];
int chain[N],cir[N],cir1[N],ans1;
int top,len[N],num[N],ans;
vector<int> a[N],inv[N];
inline int pow(int a,int b)
{
int r=1;
while (b)
{
if (b&1)r=(ll)r*a%mo;
a=(ll)a*a%mo;
b>>=1;
}
return r;
}
inline void pre()
{
fo(i,1,n)
{
a[i].pb(1);
inv[i].pb(1);
for(int j=1;i*j<=n;j++)
{
int tmp=(ll)a[i][j-1]*i%mo;
if(j>=2)tmp=(tmp+(ll)a[i][j-2]*(j-1)%mo*i%mo)%mo;
a[i].pb(tmp);
tmp=pow(tmp,mo-2);
inv[i].pb(tmp);
}
}
}
inline void dp()
{
ans1=1;
fo(i,1,n)
ans1=(ll)ans1*a[i][cir[i]]%mo;
}
inline void calc(int d)
{
int tmp=ans1;
fo(i,1,top)
{
int l=len[i];
tmp=(ll)tmp*inv[l][cir[l]]%mo;
cir[l]++,cir1[min(l,3)]++;
tmp=(ll)tmp*a[l][cir[l]]%mo;
}
if (cir1[3])d=(ll)d*tmp%mo;
ans=(ans+d)%mo;
fo(i,1,top)
{
int l=len[i];
cir[l]--,cir1[min(l,3)]--;
}
}
inline void dfs(int now,int d)
{
if (now==k)
{
calc(d);
return;
}
len[++top]=chain[now];
num[top]=1;
dfs(now+1,d);
num[top--]=0;
fo(i,1,top)
{
len[i]+=chain[now];
num[i]++;
dfs(now+1,(ll)d*(num[i]-1)%mo);
num[i]--;
len[i]-=chain[now];
}
}
inline void solve()
{
pre();
int tot=0;
fo(i,1,n)
if (!bz[i])
{
int x=i,y=0;
while (x)
{
vis[x]=1;
x=p[x];
++y;
}
chain[tot]=y;
tot++;
}
fo(i,1,n)
if(!vis[i])
{
int x=i,y=1;
while (x)
{
vis[x]=1;
if (p[x]==i)break;
x=p[x];
y++;
}
++cir[y];
++cir1[min(y,3)];
}
dp();
dfs(0,1);
}
int main()
{
freopen("sort.in","r",stdin);
freopen("sort.out","w",stdout);
scanf("%d%d",&n,&k);
fo(i,1,n)
{
scanf("%d",&p[i]);
bz[p[i]]=1;
}
solve();
printf("%d\n",ans);
return 0;
}