題目大意:你須要構造一個長度爲$n$的排列$A$,使得裏面包含有子序列$B$(子序列$B$爲一個給定的$1$到$m$的排列),且對於每一個$i$,有$A[A[i]]=i$,問有多少種方案方案。c++
數據範圍:$n≤10^7$,$m≤500$,答案對$10^9+7$取模spa
咱們首先不考慮有m的存在,考慮如何構造一個符合條件的序列$A$。code
咱們發現咱們能夠DP,設$f[i]$表示有多少種長度爲i的序列知足$A[A[i]]=i$。blog
對於第$i$個數,咱們能夠考慮把它填在原位,或者放在第j個位置,而後在$A[i]$處填上$j$。it
根據這個,不難推出$f[i]=(i-1)f[i-2]+f[i-1]$。class
咱們下面考慮,序列$B$的前$k$個數,位於$A$中前$m$個位置,剩餘的$m-k$個,位於後$n-m$個位置。im
咱們發現,對於剩餘的$m-k$個數,對應的$A_B[i]$,都會被佔用,用來填(填寫了$B[i]$的位置)。數據
因此咱們須要在前$m$個位置裏,用剛好$k$個位置,按順序填出序列$B$前$k$個數字。di
不難發現,至多隻有一種填法。時間
對於$B[i]$,咱們找到序列$A$中第i個能夠填數的地方,直接填入$B[i]$便可。
最後$O(m)$掃一遍判斷便可。
若是可行,那麼剩下的$n-k$個位置中,須要找出$m-k$個位置放置$B[k+1],B[k+2].....B[m]$,而且在$A[B[k+1]]$等地方填上它們的位置。
剩下的$n-2m+k$個位置,就能夠隨便填>m的數了,方案數顯然是$f[n-2m+k]$
因此方案數爲$\binom{n-k}{m-k}\times f[n-2m+k]$。
咱們對於每一個不超過m的k所有處理一遍就能夠了。
時間複雜度:$O(n+m^2)$
1 #include<bits/stdc++.h> 2 #define MOD 1000000007 3 #define L long long 4 #define M 10000005 5 using namespace std; 6 7 L fac[M]={0},invfac[M]={0},f[M]={0}; 8 L pow_mod(L x,L k){L ans=1;for(;k;k>>=1,x=x*x%MOD) if(k&1) ans=ans*x%MOD; return ans;} 9 L C(int n,int m){return fac[n]*invfac[m]%MOD*invfac[n-m]%MOD;} 10 11 L mark[M]={0},b[M]={0},a[M]={0},ans=0,n,m; 12 void solve(int k){ 13 if(n-m<m-k) return; 14 for(int i=1,j=1;i<=k;i++,j++){ 15 while(!mark[j]) j++; 16 a[j]=b[i]; 17 } 18 for(int i=1;i<=m;i++) 19 if(mark[i]&&a[a[i]]!=i) return; 20 ans=(ans+C(n-m,m-k)*f[n-m-m+k])%MOD; 21 } 22 int main(){ 23 fac[0]=1; for(int i=1;i<M;i++) fac[i]=fac[i-1]*i%MOD; 24 invfac[M-1]=pow_mod(fac[M-1],MOD-2); 25 for(int i=M-2;~i;i--) invfac[i]=invfac[i+1]*(i+1)%MOD; 26 f[0]=f[1]=1; for(int i=2;i<M;i++) f[i]=(f[i-1]+f[i-2]*(i-1))%MOD; 27 28 scanf("%d%d",&n,&m); 29 for(int i=1;i<=m;i++) scanf("%d",b+i); 30 for(int k=0;k<=m;k++){ 31 mark[b[k]]=1; 32 solve(k); 33 } 34 cout<<ans<<endl; 35 }