题意:
- 给出n,m,k,询问有多少个长度为n的数组
b 满足以下要求:
- b[i]≤b[i+1](1≤i<n)
- 1≤b[1]≤b[n]≤m
- 将b数组排列后有大于等于一种小于等于
k 种排列a满足以下要求:
|a[i]−a[i+1]|=1(1≤i<n) - |a[n]−a[1]|=1
- a[1]=minni=1a[i]
- n,m,k≤100
题解:
- 首先我们可以假设a[1]=1,最后将答案乘上m−i+1即可。
- 不妨破环成链,设a[n+1]=a[1]。
- 设dp[i][j][k][l]表示目前最大数为i,已经有
j 个数,有k个空必须加数,满足的排列数已有l 种的方案数。 - 转移比较显然:
- 枚举i+1的个数t。
dp[i][j][k][l]−>dp[i+1][j+t][t−k][l∗Ck−1t−1] - 注意,只有一个数显然是不可以的(因为我们开始已经让n加一了)。
代码:
#include <bits/stdc++.h> #define ll long long #define mod 1000000007 #define N 109 using namespace std; int c[N][N],n,m,K,dp[2][N][N][N],Ans; void add(int &x,int y) { x=(x+y>=mod)?x+y-mod:x+y; } int main() { ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); cin>>n>>m>>K; c[0][0]=1; for (int i=1;i<=K;i++) for (int j=0;j<=i;j++) { c[i][j]=(j?c[i-1][j-1]:0)+c[i-1][j]; if (c[i][j]>K) c[i][j]=K+1; } n++; int now=1,last=0; dp[now][0][1][1]=1; for (int i=0;i<=m;i++) { last=now,now^=1; if (i) { int tmp=0; for (int j=2;j<=n;j++) for (int l=1;l<=K;l++) add(tmp,dp[last][j][0][l]); add(Ans,(ll)tmp*(m-i+1)%mod); } if (i==m) break; memset(dp[now],0,sizeof(dp[now])); for (int j=0;j<=n;j++) for (int k=1;k<=n;k++) for (int l=1;l<=K;l++) if (dp[last][j][k][l]) for (int t=k;t<=n-j;t++) if (l*c[t-1][k-1]<=K) add(dp[now][j+t][t-k][l*c[t-1][k-1]],dp[last][j][k][l]); } cout<<Ans<<'\n'; return 0; }