[CF301E]Yaroslav and Arrangements
阿新 • • 發佈:2019-02-01
題目大意
一個長度為n的序列a如果a[1]是最小的且每一個a[i]都與a[i%n+1]相差1稱a是紅衣男孩。
一個長度在n以內的序列b如果每一個b[i]都在[1,m]內且b是非遞減的,而且重排後是紅衣男孩的序列不超過p個且至少有1個(重排後算的是本質不同的序列個數)則稱b是藍衣男孩。
求藍衣男孩的個數。
DP
你想想假如有一堆數了。我們先認為最小數是1,到時候乘上m-最大元素+1即可。
首先只考慮1有一堆1
每兩個1中間一定要夾有2,而且如果1之間的2不止1個,這些2之間還有有3。
依次類推,我們可以想到dp。當然注意原問題最後一個必須是2,我們可以在後面加一個1。
設f[i,j,k,l]表示最大元素為i,b有j個數,一共有k個間隙(即k對i中間需要一定有i+1),方案數為l。
列舉i+1的個數t,容易寫出轉移到f[i+1,j+t,t-k,l* k−1t−1]
統計答案也很簡單了。
注意只有1個元素是不合法的。
詳見程式碼。
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn=100+10,mo=1000000007;
int f[2][maxn][maxn][maxn],c[maxn][maxn];
int i,j,k,l,t,n,m,p,ans,num,now;
int main(){
//freopen("arrangement.in","r",stdin);freopen("arrangement.out","w",stdout);
c[0][0]=1;
fo(i,1,100){
c[i][0]=1;
fo(j,1,100){
c[i][j]=c[i-1][j]+c[i-1][j-1];
if (c[i][j]>100) c[i][j]=101;
}
}
scanf("%d%d%d",&n,&m,&p);
n++;
f[0][0][1][1]=1;
fo(i,0,n){
num=0;
fo(j,2,n)
fo(l,1,p)
(num+=f[now][j][0][l])%=mo;
if (m-i+1>0) ans=(ans+(ll)num*(m-i+1)%mo)%mo;
fo(j,0,n)
fo(k,0,n)
fo(l,0,p)
f[1-now][j][k][l]=0;
fo(j,0,n)
fo(k,1,n)
fo(l,1,p)
if (f[now][j][k][l])
fo(t,k,n-j)
if (l*c[t-1][k-1]<=p) (f[1-now][j+t][t-k][l*c[t-1][k-1]]+=f[now][j][k][l])%=mo;
now=1-now;
}
printf("%d\n",ans);
}