1. 程式人生 > >[CF403D]Beautiful Pairs of Numbers

[CF403D]Beautiful Pairs of Numbers

相同 left return 組合數 can long line 基礎 IT

題意:給定$n,k$,對於整數對序列$\left(a_1,b_1\right),\cdots,\left(a_k,b_k\right)$,如果$1\leq a_1\leq b_1\lt a_2\leq b_2\lt\cdots\lt a_k\leq b_k\leq n$且所有的$b_i-a_i$互不相同,則稱這個序列是“美麗的”,求美麗的序列的個數

先轉化一下,把每個數對$\left(a_i,b_i\right)$看作一個區間$\left[a_i,b_i\right]$,則題目要求的是$k$個長度不同的區間互不重疊地放置在$[1,n]$的方案數

記$f_{i,j}$表示$i$個長度不同的區間總長為$j$且按長度遞增排列的方案數,則$f_{0,0}=1,f_{i,j}=f_{i,j-i}+f_{i-1,j-i}$(可以直接把原方案的每個區間長度$+1$,或者在這個基礎上再增加一個長度為$1$的區間)

那麽答案是$k!\sum\limits_{i=1}^n\binom{n-i+k}kf_{k,i}$

相當於是枚舉所有區間的總長,對於總長為$i$的所有方案($f_{k,i}$),把區間塞到$[1,n]$後我們還有$n-i$的空隙,相當於計算有$k$個區間和$n-i$個空隙的排列數($\binom{n-i+k}k$),因為$f$計數的是按長度遞增排列的方案數,所以最後乘上$k!$表示任意排列

以上所有的東西(組合數,$f$,答案)都可以預處理出來,然後$O(1)$回答詢問即可

不是這樣的我沒有在刷水題

#include<stdio.h>
typedef long long ll;
const int mod=1000000007,N=1000,K=50;
int fac[1010],rfac[1010],f[60][1010],ans[1010][60];
int mul(int a,int b){return a*(ll)b%mod;}
int ad(int a,int b){return(a+b)%mod;}
int C(int n,int k){return mul(fac[n],mul(rfac[n-k],rfac[k]));}
int pow(int a,int b){
	int s=1;
	while(b){
		if(b&1)s=mul(s,a);
		a=mul(a,a);
		b>>=1;
	}
	return s;
}
int main(){
	int i,j,k,t,n;
	fac[0]=1;
	for(i=1;i<=N;i++)fac[i]=mul(fac[i-1],i);
	rfac[N]=pow(fac[N],mod-2);
	for(i=N;i>0;i--)rfac[i-1]=mul(rfac[i],i);
	f[0][0]=1;
	for(i=1;i<=K;i++){
		for(j=i*(i+1)/2;j<=N;j++)f[i][j]=ad(f[i][j-i],f[i-1][j-i]);
	}
	for(i=1;i<=N;i++){
		for(k=1;k<=K;k++){
			for(j=k*(k+1)/2;j<=i;j++){
				ans[i][k]=ad(ans[i][k],mul(C(i-j+k,k),f[k][j]));
			}
			ans[i][k]=mul(ans[i][k],fac[k]);
		}
	}
	scanf("%d",&t);
	while(t--){
		scanf("%d%d",&n,&k);
		if(k>K)
			puts("0");
		else
			printf("%d\n",ans[n][k]);
	}
}

[CF403D]Beautiful Pairs of Numbers