Newcoder 83 D.佇列重排(dp+組合數學)
阿新 • • 發佈:2018-12-16
Description
有 個人排成一列,把他們解散後重排,使得"重排後前方" 跟"原排列前方" 一樣的人不超過 個,問有幾種方法數,答案請 輸出。
舉例來說,有五個人編號為∼ 間的整數,最初的排列由前至後依序為,重排列後順序由前至後變為,其中只有編號為 的人,“原排列前方” 跟"重排後前方" 都是編號為 的人,故"重排後前方" 跟"原排列前方" 一樣的人只有人。
原排列的第個人和重排後的第個人一定不會是「“重排後前方” 跟 “原排列前方” 一樣的人」。
Input
輸入只有一行,有兩個正整數,滿足。
Output
請輸出一行包含一個小於$10^9+7 $的非負整數,表示總共的方法數 。
Sample Input
3 1
Sample Output
5
Solution
令為個人重排後前方的人均不是原排列前方人的方案數,假設起初排位為,考慮的作用
若不再任意一對之間,那麼需要~這個人重排後前方的人均不是原排列前方的人,方案數為,對於這其中的每一種方案,只要不在前方即可,故有個位置可以放
若在某對之間,選擇一個方案數為,之後把看作一個元素,問題轉化為個人重排後每個人前方的人均不是原排列前方的人,方案數
綜上有,線性預處理即可
之後考慮原問題,假設重排後有個人前方的人和原排列前方的人相同,從個有前方人的人中選出這個人方案數,選出後,把這對看作個元素,問題轉化為個人重排後每個人前方的人均不是原排列前方人的方案數,即,故答案為
Code
#include<cstdio>
using namespace std;
typedef long long ll;
const int maxn=500005;
#define mod 1000000007
int add(int x,int y)
{
x+=y;
if(x>=mod)x-=mod;
return x;
}
int mul(int x,int y)
{
ll z=1ll*x*y;
return z-z/mod*mod;
}
int n,k,a[maxn],inv[maxn],fact[maxn];
void init(int n=5e5)
{
inv[1]=1;
for(int i=2;i<=n;i++)inv[i]=mul(mod-mod/i,inv[mod%i]);
inv[0]=1;
for(int i=1;i<=n;i++)inv[i]=mul(inv[i-1],inv[i]);
fact[0]=1;
for(int i=1;i<=n;i++)fact[i]=mul(i,fact[i-1]);
}
int C(int n,int m)
{
return mul(fact[n],mul(inv[m],inv[n-m]));
}
int main()
{
init();
scanf("%d%d",&n,&k);
a[1]=1,a[2]=1;
for(int i=3;i<=n;i++)a[i]=add(mul(i-1,a[i-1]),mul(i-2,a[i-2]));
int ans=0;
for(int i=0;i<=k;i++)ans=add(ans,mul(C(n-1,i),a[n-i]));
printf("%d\n",ans);
return 0;
}