P6533 [COCI2015-2016#1] RELATIVNOST 题解

news/2024/10/19 14:12:16


考虑当 $q = 0$ 时怎么做。

注意到性质 $c \le 20$,因此不妨正难则反,将**至少有 $c$ 个人购买彩色画**的方案数转化为总方案数减去**不足 $c$ 人购买彩色画的方案数**。

这个是一个类似凑数的 dp,不妨考虑背包。我们有 $f_{i, j}$ 表示前 $i$ 人中**恰好** $j$ 人购买彩色画的方案数。

对于当前人,可以选择买或者不买彩色画,所以有转移

$$f_{i, j} = f_{i - 1, j - 1} \times a_i + f_{i - 1, j} \times b_i$$

>注意到 $f_i$ 只跟 $f_{i - 1}$ 这一维有关,可以进行滚动。

总答案数,根据乘法原理和加法原理有 $tot = \displaystyle\prod_{i = 1}^n (a_i + b_i)$。

当 $q \ne 0$ 时,我们需要支持修改。但别忘了上面 dp 的本质还是背包,而背包是支持合并的,于是我们可以把背包放到线段树上,对于每个节点维护一个背包,在 pushup 时进行合并。

复杂度 $O(nc^2 + c^2q \log n)$。

代码:
```cpp
// ubsan: undefined
// accoders
// 如果命运对你缄默, 那就活给他看。
#pragma GCC optimize(1)
#pragma GCC optimize(2)
#pragma GCC optimize(3)
#pragma GCC optimize("Ofast", "inline", "-ffast-math")
#pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include <iostream>
using namespace std;
typedef long long LL;
// #define int LL
const int p = 1e4 + 7;
void read(int &var) {
    var = 0;
    int f = 1;
    char c;
    do { c = getchar();
        if (c == '-') f = -1;
    } while (c < 48 || c > 57);
    while (c >= 48 && c <= 57) var = var * 10 + (c - '0'), c = getchar();
    var *= f;
};
void write(int x) {
    if (x < 0) putchar('-'), x = -x;
    if (x > 9) write(x / 10);
    putchar(x % 10 + '0');
}
const int maxc = 21;
const int maxn = 1e5 + 10;
struct Node {
    int f[maxc];
    int tot;
    int ls, rs;
} t[maxn << 2];
int n, c;
int a[maxn], b[maxn];
int tot;
int root;
int q;
inline void pu(int u) {
    Node& rt = t[u];
    Node ls = t[t[u].ls], rs = t[t[u].rs];
    for(int i = 0; i < c; ++ i) rt.f[i] = 0;
    for(int i = 0; i < c; ++ i)
        for(int j = 0; j + i < c; ++ j) rt.f[i + j] = ((LL)rt.f[i + j] + 1LL * ls.f[i] * rs.f[j] % p) % p;
    rt.tot = 1LL * ls.tot * rs.tot % p;
}
inline void build(int& u, int l, int r) {
    if(!u) u = ++ tot;
    if(l == r) {
        t[u].f[1] = a[r];
        t[u].f[0] = b[r];
        t[u].tot = a[r] + b[r];
        return ;
    }
    int mid = l + r >> 1;
    build(t[u].ls, l, mid);
    build(t[u].rs, mid + 1, r), pu(u);
}
inline void upd(int u, int l, int r, int p) {
    if(l == r) {
        t[u].f[1] = a[r];
        t[u].f[0] = b[r];
        t[u].tot = a[r] + b[r];
        return ;
    }
    int mid = l + r >> 1;
    if(p <= mid) upd(t[u].ls, l, mid, p);
    else upd(t[u].rs, mid + 1, r, p);
    pu(u);
}
signed main() {
    // freopen("balloon.in", "r", stdin);
    // freopen("balloon.out", "w", stdout);
    read(n), read(c);
    for(int i = 1; i <= n; ++ i) read(a[i]);
    for(int i = 1; i <= n; ++ i) read(b[i]);
    build(root, 1, n);
    read(q);
    for(int p, x, y; q --; ) {
        read(p), read(x), read(y);
        a[p] = x, b[p] = y;
        upd(root, 1, n, p);
        int ans = t[1].tot;
        for(int i = 0; i < c; ++ i) {
            ans = ((ans - t[1].f[i]) % :: p + :: p) % :: p;
        }
        ans = (ans + :: p) % :: p;
        write(ans); putchar('\n');
    }
    return 0;
}
```

## Ex $n, q \le 10^6$

背包除了支持合并,还支持撤销。

我们注意到一次修改操作,实质上可以转化为撤销一次和插入一次物品,于是 $O(qc)$ 的做法呼之欲出,我们对于每次操作进行撤销 + 插入的组合,实时维护当前的答案。

具体来说,回顾我们的转移
$$f_{i, j} = f_{i - 1, j - 1} \times a_i + f_{i - 1, j} \times b_i$$

进一步的滚动掉 $i$ 这维

$$f_{j} = f_{j - 1} \times a_i + f_{j} \times b_i$$

注意这时转移是倒序,那撤销时正序答案才正确,所以有

```cpp
f[0] = 1LL * f[0] * invb % mod;
        for (int i = 1; i < c; ++i) f[i] = (f[i] - f[i - 1] * a[p] % mod + mod) * invb % mod;
```

插入时与初始 dp 相同。

代码:
```cpp
// ubsan: undefined
// accoders
// 如果命运对你缄默, 那就活给他看。
// #pragma GCC optimize(1)
// #pragma GCC optimize(2)
// #pragma GCC optimize(3)
// #pragma GCC optimize("Ofast", "inline", "-ffast-math")
// #pragma GCC target("avx,sse2,sse3,sse4,mmx")
#include <iostream>
using namespace std;
typedef long long LL;
#define int LL
const int mod = 1e9 + 7;
void read(int &var) {
    var = 0;
    int f = 1;
    char c;
    do { c = getchar();
        if (c == '-') f = -1;
    } while (c < 48 || c > 57);
    while (c >= 48 && c <= 57) var = var * 10 + (c - '0'), c = getchar();
    var *= f;
};
void write(int x) {
    if (x < 0) putchar('-'), x = -x;
    if (x > 9) write(x / 10);
    putchar(x % 10 + '0');
}
inline int fpow(int a, int b) {
    int ans = 1;
    for(; b; b >>= 1, a = a * a % mod)
        if(b & 1) ans = ans * a % mod;
    return ans;
}
inline int inv(int x) { return fpow(x, mod - 2); }
const int maxc = 21;
const int maxn = 1e6 + 10;
int f[maxn], n, c, q;
int a[maxn], b[maxn], ans = 1;
signed main() {
    freopen("balloon.in", "r", stdin);
    freopen("balloon.out", "w", stdout);
    read(n), read(c); f[0] = 1;
    for(int i = 1; i <= n; ++ i) read(a[i]);
    for(int i = 1; i <= n; ++ i) {
        read(b[i]);
        ans = ans * (a[i] + b[i]) % mod;
    }
    for(int i = 1; i <= n; ++ i) {
        for(int j = c - 1; j > 0; -- j) f[j] = (f[j - 1] * a[i] % mod + f[j] * b[i] % mod) % mod;
        f[0] = (f[0] * b[i]) % mod;
    }
    read(q);
    for(int p, x, y; q --; ) {
        read(p), read(x), read(y);
        int inva = inv(a[p]), invb = inv(b[p]); // 1 / a_p, 1 / b_p
        ans = 1LL * ans * inv(a[p] + b[p]) % mod;
        ans = 1LL * ans * (x + y) % mod;
        f[0] = 1LL * f[0] * invb % mod;
        for(int i = 1; i < c; ++ i) f[i] = (f[i] - f[i - 1] * a[p] % mod + mod) * invb % mod;
        for(int i = c - 1; i > 0; -- i) f[i] = ((f[i - 1] * x % mod + f[i] * y % mod) + mod) % mod;
        f[0] = 1LL * f[0] * y % mod;
        a[p] = x, b[p] = y;
        int res = ans;
        for(int i = 0; i < c; ++ i) res = (res - f[i]) % mod;
        res = (res + mod) % mod;
        cout << res << '\n';
    }
    return 0;
}
```

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/73438.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

npm run的时候报错: this[kHandle] = new _Hash(algorithm, xofLen);

在前面加入以下配置信息 set NODE_OPTIONS=--openssl-legacy-provider && 后面跟原来的启动配置信息 凡哥,别他妈吹牛逼了

MiGPT让你的小爱音响更聪明hA

合集 - 经验分享(29)1.记一次由于操作失误致使数据库瘫痪的故障分析与解决方案2023-09-082.网络之谜:记一次失败排查的故事2023-11-153.你是否想知道如何应对高并发?Go语言为你提供了答案!2023-12-294.2023年终总结:拉帮结伙,拼搏探索新机遇2023-12-305.谁说后端不能画出美…

Nuxt.js 应用中的 app:templatesGenerated 事件钩子详解

title: Nuxt.js 应用中的 app:templatesGenerated 事件钩子详解 date: 2024/10/19 updated: 2024/10/19 author: cmdragon excerpt: app:templatesGenerated 是 Nuxt.js 的一个生命周期钩子,在模板编译到虚拟文件系统(Virtual File System, VFS)之后被调用。这个钩子允许…

链路与应用负载

为什么需要负载 如今越来越多的服务选择上云 加入到互联网 方便人们的使用 人们对服务的访问质量要求更高 对于高可靠性:电源: 往往采取双电源模式 当电源出现故障 网络不会陷入瘫痪线路: 有静态聚合 将多条线路逻辑变成一条线路 数据包会负载均衡的形式从多条逻辑成一条的链路…

HTTP客户端框架之UniHttp讲解

目录1 UniHttp1.1 简介1.1.1 前言1.1.2 简介1.2 简单使用1.2.1 引入依赖1.2.2 对接接口1.2.3 声明定义 HttpAPI 包扫描路径1.2.4 依赖注入使用即可1.3 说明介绍1.3.1 @HttpApi注解1.3.2 @HttpInterface注解1.3.3 @Par注解1.3.3.1 @QueryPar注解1.3.3.2 @PathPar注解1.3.3.3 @He…

YU_C++算法学习笔记 枚举

https://iknow-pic.cdn.bcebos.com/b3119313b07eca8047023f79832397dda144832c1.1 枚举类问题枚举是什么?枚举也叫穷举,是计算机解决问题最基本的策略。其方法是一一列举所有的可能性,根据题意要求进行合理的判断或计算,最终得到答案,本质上就是一种搜索算法基础的枚举就…

mysql问题排查常用脚本

1.查询不是sleep或者有状态的sqlselect * from information_schema.`PROCESSLIST` where command != SLEEP2.查询运行中的事务select trx_state, trx_started, trx_mysql_thread_id, trx_query from information_schema.innodb_trx3.查看进程show full processlist本篇文章如有…