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;}
- tc633div1 550分
- 分
- 分
- 分
- 分
- 一道Topcoder Algorithms 550分的题
- 专家分5分
- 分库 分表
- 1083 -- 分!分!分! 学生的命根
- 1259: 分!分!分! 学生的命根
- 分目
- 分班
- 分分分
- 接分
- 分粥
- 赚分
- 分业
- 换分
- 如何选择适合自己企业的HR软件?
- 日志快速增长导致磁盘撑爆的快速解决方法
- WinDbg-如何抓取dump文件
- java中的流
- HDU 4801 Pocket Cube(模拟题——转魔方)
- tc633div1 550分
- 采药(01背包)
- Android中View转换为Bitmap及getDrawingCache=null的解决方法
- ContentProvider 的创建以及增删改查操作(你想看的都在这里了)
- nyoj-170-网络的可靠性(找度数1)
- 深入剖析EXCHANGE SERVER的邮件存储和日志
- Hibernate中根据日期(天数)来查询
- Android开发学习笔记十六 gitHub
- Volley使用与解析