[BZOJ 5215] [Lydsy2017省隊十連測]商店購物
阿新 • • 發佈:2018-12-10
題目描述
在 Byteland一共開著 家商店,編號依次為 到 ,其中編號為 到 的商店有日消費量上限,第 家商店的日消費量上限為。Byteasar每次購物的過程是這樣的:依次經過每家商店,然後購買非負整數價格的商品,並在結賬=的時候在賬本上寫上在這家商店消費了多少錢。當然,他在這家商店也可以什麼都不買,然後在賬本上寫上一個。這一天, Byteasar日常完成了一次購物,但是他不慎遺失了他的賬本。他只記得自己這一天一共消費了多少錢,請寫一個程式,幫助 Byteasar計算有多少種可能的賬單。
輸入輸出格式
輸入格式:
第一行包含三個正整數 ,分別表示商店的個數、有限制的商店個數以及總消費量。
第二行包含 個整數,依次表示 。
輸出格式:
輸出一行一個整數,即可能的賬單數,由於答案可能很大,請對取模輸出。
輸入輸出樣例
輸入樣例#1:
3 2 8
2 1
輸出樣例#1:
6
說明
6 種方案分別為:{0; 0; 8}; {1; 0; 7}; {2; 0; 6}; {0; 1; 7}; {1; 1; 6}; {2; 1; 5}
。
,,, 。
解題分析
很顯然前半部分搞, 後半部分組合數搞。
先來看前半部分: 我們很容易想到一個的 : 設為前個點總共取到的方案數, 那麼, 但顯然沒有分。
再仔細觀察, 發現能轉移到一個點的值為字首的字尾, 因此我們每次做一次字首和, 就可以轉移, 這樣總的複雜度就是。
再來看後面的一部分, 列舉前面取到的值, 相當於我們要求塊錢分到個店裡, 且允許某個店不分錢的方案數。 相當於個板插到塊錢中間的方案數, 所以答案為。
另外, 這道題十分卡常, 取模用減的方法, 不要全部開計算, 線篩階乘逆元可以先打表得到的逆元再推回去。 詳見程式碼:
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <cctype>
#include <cstdlib>
#include <cstring>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define File freopen("shopping.in", "r", stdin), freopen("shopping.out", "w", stdout)
#define MX 100500
#define max(a, b) ((a) > (b) ? (a) : (b))
#define SIZ 10000500
#define MOD 1000000007
#define ll long long
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
for (; !isdigit(c); c = gc);
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
}
template <class T> IN void mod(T &x, int add) {x += add; if(x >= MOD) x -= MOD;}
int dp[SIZ], inv[SIZ], fac[SIZ], ans;
int n, m, goal;
int val[MX];
IN int C(R int n, R int m)
{
if(n < 0) return 1;
if(m < 0) return 0;
return 1ll * fac[n] * inv[m] % MOD * inv[n - m] % MOD;
}
int main(void)
{
File;
in(n), in(m), in(goal); dp[0] = 1;
R int i, j; int bd = 300 * m, tar, sum = 0;
for (i = 1; i <= m; ++i) in(val[i]);
for (i = 1; i <= m; ++i)
{
sum += val[i]; int v = val[i] + 1;
for (j = sum - val[i]; ~j; --j) mod(dp[j + v], MOD - dp[j]);
for (j = 1; j <= bd; ++j) mod(dp[j], dp[j - 1]);
}
inv[10000000] = 979208068;
bd = goal + n - m; inv[0] = inv[1] = fac[0] = 1;
for (i = 9999999; i; --i) inv[i] = 1ll * inv[i + 1] * (i +1) % MOD;
for (i = 1; i <= bd; ++i) fac[i] = 1ll * fac[i - 1] * i % MOD;
for (i = 0; i <= goal; ++i)
(ans += 1ll * dp[i] * C(goal - i + n - m - 1, n - m - 1) % MOD) %= MOD;
printf("%d", ans);
}