2018 ICPC 沈阳站 M. Renaissance Past in Nancy 题解

M. Renaissance Past in Nancy

题目链接
题意:
T ≤ 10 T \le 10 T10。有 n ≤ 1 0 4 n \le 10^4 n104种物品,每种物品数量和体积分别为 1 ≤ a , b ≤ 1 0 3 1\le a,b\le10^3 1a,b103 m ≤ 1 0 4 m\le10^4 m104询问,每次询问区间 1 ≤ l , r ≤ n 1\le l,r \le n 1l,rn的物品装体积为 c ≤ 1 0 3 c\le 10^3 c103的方案数。
可逆背包:
对于背包问题,可以逆向转移而达到移除一个物品的贡献。
计数背包可逆,而求max背包不可逆。
背包与生成函数:
01背包: F ( x ) = 1 + x b F(x)=1+x^b F(x)=1+xb
01退背包: F ( x ) = 1 − x b F(x)=1-x^b F(x)=1xb
完全背包: F ( x ) = 1 1 − x b F(x)=\frac{1}{1-x^b} F(x)=1xb1
多重背包: F ( x ) = 1 − x ( a + 1 ) b 1 − x b F(x)=\frac{1-x^{(a+1)b}}{1-x^b} F(x)=1xb1x(a+1)b
一堆物品生成函数: G ( x ) = ∏ 所 有 物 品 F ( x ) G(x)=\prod_{所有物品} F(x) G(x)=F(x)
一堆物品,背包体积为 c c c的方案数 ∑ i = 0 v g i = ( G ( x ) 1 − x ) [ c ] \sum_{i=0}^vg_i=(\frac{G(x)}{1-x})[c] i=0vgi=(1xG(x))[c]
H ( x ) = ∑ i = 0 h i x i H(x)=\sum_{i=0}h_ix^i H(x)=i=0hixi H ( x ) 1 − x = ∑ i = 0 ∑ j = 0 i h j x i \frac{H(x)}{1-x}=\sum_{i=0}\sum_{j=0}^ih_jx^i 1xH(x)=i=0j=0ihjxi
本题:
记第 i i i个物品的生成函数为 F i ( x ) F_i(x) Fi(x) G i ( x ) = ∏ j = 1 i F j ( x ) G_i(x)=\prod_{j=1}^iF_j(x) Gi(x)=j=1iFj(x)
特别的 G 0 ( x ) = 1 G_0(x)=1 G0(x)=1
那么答案等于 ( 1 1 − x G r ( x ) ∗ 1 G l − 1 ( x ) ) [ c ] (\frac{1}{1-x}G_{r}(x)*\frac{1}{G_{l-1}(x)})[c] (1x1Gr(x)Gl1(x)1)[c]
G i ( x ) G_i(x) Gi(x)的求法:
从生成函数的角度,多重背包等价于体积为 b b b的完全背包体积为 ( a + 1 ) b (a+1)b (a+1)b的01退背包组成。
用背包DP的方法维护 G i ( x ) G_i(x) Gi(x) O ( c ) O(c) O(c)添加一个物品。
1 G i ( x ) \frac{1}{G_i(x)} Gi(x)1的求法:
类似的,多重背包的逆等价于体积为 ( a + 1 ) b (a+1)b (a+1)b的完全背包体积为 b b b的01退背包组成。
关于询问:
每次询问求一次卷积再求前缀和,复杂度太高。
预处理出 1 1 − x 1 G i ( x ) \frac{1}{1-x}\frac{1}{G_{i}(x)} 1x1Gi(x)1 1 1 − x G i ( x ) \frac{1}{1-x}G_{i}(x) 1x1Gi(x),(就是求前缀和啦), O ( n c ) O(nc) O(nc);每次询问就只用求 ( 1 1 − x G r ( x ) ∗ 1 G l − 1 ( x ) ) (\frac{1}{1-x}G_{r}(x)*\frac{1}{G_{l-1}(x)}) (1x1Gr(x)Gl1(x)1)的第 c c c项, O ( c ) O(c) O(c)解决。
最后,感谢这篇博客给的提示。
Code:

#include<bits/stdc++.h>

using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;

void insert(vector<int> &f, int a) {
    for (int i = a; i <= f.size() - 1; i++) {
        f[i] = (f[i] + f[i - a]) % mod;
    }
}

void remove(vector<int> &f, int a) {
    for (int i = f.size() - 1; i >= a; i--) {
        f[i] = (f[i] - f[i - a] + mod) % mod;
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    int _;
    cin >> _;
    while (_--) {
        int n, m;
        cin >> n >> m;
        const int V = 1000;
        vector<vector<int>> f(n + 1, vector<int>(V + 1)), g(f);
        f[0][0] = g[0][0] = 1;
        for (int i = 1; i <= n; i++) {
            int a, b;
            cin >> b >> a;//b个a
            f[i] = f[i - 1];
            g[i] = g[i - 1];
            insert(f[i], a);
            remove(f[i], a * (b + 1));
            remove(g[i], a);
            insert(g[i], a * (b + 1));
        }
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= V; j++)
                f[i][j] = (f[i][j] + f[i][j - 1]) % mod;
        }
        int last = 0;
        static int Case = 0;
        cout << "Case #" << ++Case << ":\n";
        while (m--) {
            int l, r, c;
            cin >> l >> r >> c;
            int t1 = (l + last) % n + 1;
            int t2 = (r + last) % n + 1;
            l = min(t1, t2), r = max(t1, t2);
            int res = 0;
            for (int i = 0; i <= c; i++)
                res = (res + (ll) g[l - 1][i] * f[r][c - i]) % mod;
            last = res;
            cout << res << "\n";
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值