题目描述:
给定原来由一些1~D面值的硬币凑出0~D面值的方案数模1e9+7,询问q次,每次询问移出number个value面值的硬币后凑出D面值的方案数。
每个硬币包括面值一样的硬币都是不同的.
要求q*D的时间复杂度.
题解:
对于每一个询问,需要d的复杂度算出来dp[d]. 先考虑简单的递推,如果number=1,那么其实dp[d] = f[d]-dp[d-1] 就行了. 这样dp过去.但是如果number变大,每次都要往前边搞一个组合数,就成d*d的了. 这样不行.然后想要维护带组合数权值的前缀和,发现不会. 既然这样,我们只用算dp[d]就行了. 能不能推出dp[d]和初始值f之间的关系,就是不用dp[d-1]. 这样去写公式,发现又是组合数一堆. 但是遇到这种情况我们以后都这样处理:组合数的公式其实可以一次一次的差分求和搞出来. 我们每次只去掉一个硬币,那么dp[d] = dp[d-1] + 上一层的dp[d]. 只看比如f0的系数的绝对值,发现其实是这一层d-1的值加上上一层d的值组成这一层d的值,就是说把上一层当成了差分!!! 每一层f0的初始值都是1, 这样 差分一次就是111111
差分两次就是12345 差分三次就是1 3 6… 所以每次f0的系数权值都是:C[number-1+d][number-1], 并且f1对dp[d]的贡献度其实是f0的前一项. 这样就直接算出了每一个的权值,就可以o(d)的计算了.
上面是value=1的分析. 如果value = 2 …, 我们发现和d有关的f只有f[d],f[d-value], f[d-2*value]…. 先把这些数筛出来,然后就和上面的一摸一样了.
以上是差分,思路特别顺,就是为了找每一项的权值,发现有组合数不好弄,于是先算number=1哪一层的,发现有差分的性质,写一写就得出结论.
下面是另一种思路:
每个询问的生成函数多项式可以看做是对原来的多项式除一个(1+x^value)^number,也就是乘以(1+x^value)^(-number).之后求x^d的系数就行了. 涉及到展开C[-number][1], C[-number][2] . C[-n][k] = (-1)^k*C[k+n-1][k] = (-1)^k*C[k+n-1][n-1]. 这样算出来每一个的系数是C[一个数][number-1], 正负和k有关, 然后value不是1的时候之和d-value整数倍的f有关.
重点:
(1)最关键的.首先dp时间太多不能推过去要去想权值前缀和,发现组合数权值不能求, 那么一定要转化成用最初的f来直接表示dp[d], 现在使用推公式技巧,假设number=1, 那么发现i位置f0的权值是由i-1的权值加上上一次i位置f0的权值得到的. 并且最开始差分是0, 然后是 1,然后就是累加的了. 于是观察规律就能够得到.
(2)转化:当value不是1的时候,挑出来会影响d的值,之后转化成和以前一样.
(3)用母函数的方法来表示 和等于 多少 的种类数. 用来推导公式. 并且用到负数组合数的公式.
代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <ctype.h>
#include <limits.h>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <bitset>
#define CLR(a) memset(a, 0, sizeof(a))
#define REP(i, a, b) for(int i = a;i < b;i++)
#define REP_D(i, a, b) for(int i = a;i <= b;i++)
typedef long long ll;
using namespace std;
const int MOD = 1e9+7.1;
const int maxn = 3000 + 10;
int dp[maxn];
int jcrev[maxn];
int jc[maxn];
class ChangingChange
{
public:
int pow_mod(int x, int n)
{
int res = 1;
int temp = x%MOD;
while(n)
{
if(n&1)
{
res = (res*temp)%MOD;
temp = (temp*temp)%MOD;
n >>= 1;
}
}
return res;
}
public:
void getJc()
{
jcrev[0] = pow_mod(1, MOD-2);
jc[0] = 1;
for(int i = 1; i<=1000000; i++)
{
jcrev[i] = (jcrev[i-1]*pow_mod(i, MOD-2))%MOD;
jc[i] = (jc[i-1]*i)%MOD;
}
}
public:
int C(int n, int m)
{
int t = ((jc[n]*jcrev[m]%MOD)*jcrev[n-m])%MOD;
return t;
}
public:
vector <int> countWays(vector <int> ways, vector <int> valueRemoved, vector <int> numRemoved)
{
vector<int> res;
int d = ways.size()-1;
getJc();
for(int i = 0;i<valueRemoved.size();i++)
{
vector<int> f;
int x = valueRemoved[i], num = numRemoved[i];
int start = d - d/x*x;
for(int i = start;i<=d;i += x)
{
f.push_back(ways[i]);
}
int ans = f[f.size()-1];
int k = num - 1;
int now = k;
int fuhao = -1;
for(i = f.size()-2;i>=0;i--)
{
ans = ((ans+fuhao*C(now, k))%MOD + MOD)%MOD;
}
res.push_back(ans);
}
}
};
int main()
{
freopen("2Bin.txt", "r", stdin);
//freopen("3Bout.txt", "w", stdout);
return 0;
}