[Codeforces301E][DP]Yaroslav and Arrangements
阿新 • • 發佈:2018-12-14
翻譯
如果一個數列相鄰兩項之差的絕對值均為 1(我們認為首項和末項也相 鄰),並且首項是數列中最小的元素之一,那麼我們稱之為良好數列。 如果一個數列單調不降且長度在 1 到 n 之間,數列中每個數的值在 1 到 m 之間,且重排後能得到至少 1 個至多 k 個良好數列,那麼我們稱之為優秀數列。 給出 n、m、k,求優秀數列的個數。 答案對 1000000007 取模
題解
這題有點東西… 把首項看成1,破環成鏈,a[n+1]=1 如果在最大值為i的情況下,最後乘一個就好了 我們考慮如何設計狀態 首先你至少要知道當前這個數列能組成多少個良好數列 從小往大加數 比如兩個1之間如果有空格,他們之間至少要加一個2 所以我們再加入一個狀態,當前必須要加多少個數 啊別忘了記錄當前加入了多少個數 列舉當前加入的數的數量 設他為T 設至少要加K個數 顯然,我們會有T-K個組合兩兩之間是空格 所以 等等,還沒轉移方案數 其實就相當於T個球要放入K個箱子中不允許有空 那不就是 然後列舉到一個最大值記錄答案即可
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#define LL long long
#define mp(x,y) make_pair(x,y)
#define mod 1000000007
using namespace std;
inline int read()
{
int f=1,x=0;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
inline void write(int x)
{
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar(x%10+'0');
}
inline void pr1(int x){write(x);printf(" ");}
inline void pr2(int x){write(x);puts("");}
int n,m,K;
int C[105][105];
int f[2][105][105][105];//當前最大值 已經加了多少個數 還必須要加多少個數 方案數
void ad(int &x,int y){x+=y;if(x>=mod)x-=mod;}
int main()
{
n=read();m=read();K=read();
if(n==1)return puts("0"),0;
C[0][0]=1;
for(int i=1;i<=100;i++)
for(int j=0;j<=100;j++)
{
C[i][j]=j?C[i-1][j]+C[i-1][j-1]:C[i-1][j];
if(C[i][j]>K)C[i][j]=K+1;
}
n++;
int now=0,ans=0;f[now][0][1][1]=1;
for(int i=0;i<=m;i++)
{
now^=1;int num=0;
if(i)
{
for(int j=2;j<=n;j++)
for(int k=1;k<=K;k++)
ad(num,f[now^1][j][0][k]);
ad(ans,(LL)num*(m-i+1)%mod);
}
memset(f[now],0,sizeof(f[now]));
for(int j=0;j<=n;j++)
for(int k=1;k<=n;k++)
for(int l=1;l<=K;l++)
if(f[now^1][j][k][l])
for(int t=k;t+j<=n;t++)
ad(f[now][j+t][t-k][min(K+1,l*C[t-1][k-1])],f[now^1][j][k][l]);
}
pr2(ans);
return 0;
}