tc633div1 550分

来源:互联网 发布:中国m2最新数据 编辑:程序博客网 时间:2024/06/01 07:13

题目描述:

给定原来由一些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;}
0 0
原创粉丝点击