(切)糕(動歸)
阿新 • • 發佈:2018-07-22
math main 排好序 pri begin ace void code ++i
(切)糕(動歸)
一個集合的價值為其中的最大數減去最小數。給定n個數,請問有多少種劃分集合的方案,使得集合的總價值小於k?
我們先把所有元素排好序。由於一個數必須被選,我們可以定義狀態\(f[i][j][k]\),表示選到第i個數,未結束集合數為j,集合總價值為k的方案數。由於一個數可以開啟一個集合,關閉一個集合,中繼當前的任何一個集合,也可以獨占一個集合,因此一共有四種轉移:\(f[i][j][k]=\begin{aligned} f[i-1][j-1][k-(a[i]-a[i-1])*(j-1)] \\ +f[i-1][j][k-(a[i]-a[i-1])*j]*j \\ +f[i-1][j+1][k-(a[i]-a[i-1])*(j+1)]*(j+1) \\ +f[i-1][j][k-(a[i]-a[i-1])*j] \end{aligned}\)
似乎這是一種集合計數題的常見套路呢……
#include <cstdio> #include <algorithm> using namespace std; typedef long long LL; const LL maxn=205, maxk=1005, mod=1e9+7; LL f[maxn][maxn][maxk]; LL a[maxn], n, K; void up(LL i1, LL j1, LL k1, LL i2, LL j2, LL k2, LL x){ if (i2<0||j2<0||k2<0) return; f[i1][j1][k1]+=f[i2][j2][k2]*x; f[i1][j1][k1]%=mod; } int main(){ scanf("%lld%lld", &n, &K); for (LL i=1; i<=n; ++i) scanf("%lld", &a[i]); sort(a+1, a+n+1); f[0][0][0]=1; for (LL i=1; i<=n; ++i) for (LL j=0; j<=n; ++j) for (LL k=0; k<=K; ++k){ up(i,j,k,i-1,j-1,k-(a[i]-a[i-1])*(j-1),1); up(i,j,k,i-1,j,k-(a[i]-a[i-1])*j,j); up(i,j,k,i-1,j,k-(a[i]-a[i-1])*j,1); up(i,j,k,i-1,j+1,k-(a[i]-a[i-1])*(j+1),j+1); } LL ans=0; for (LL k=0; k<=K; ++k) ans+=f[n][0][k], ans%=mod; printf("%lld\n", ans); return 0; }
安拉胡阿克巴!
(切)糕(動歸)