首页 > 其他分享 >P6533 [COCI2015-2016#1] RELATIVNOST 题解

P6533 [COCI2015-2016#1] RELATIVNOST 题解

时间:2024-10-19 13:58:44浏览次数:9  
标签:GCC int 题解 COCI2015 RELATIVNOST read pragma ans mod



考虑当 $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; } ```

标签:GCC,int,题解,COCI2015,RELATIVNOST,read,pragma,ans,mod
From: https://www.cnblogs.com/Rainsheep/p/18475810

相关文章

  • [CF1616H] - Keep XOR Low 的题解
    一道比较神奇的题目,状态显得比较扯淡,但是就是能过!先建立出trie树,设\(dp_u\)表示以\(u\)为根的子树内的答案。但我们发现,若\(x\)的当前位为\(1\),那么问题就没法根据他的左右子树求解了,怎么办呢。考虑一个很扯淡的状态,设\(dp_{u,v}\)表示考虑了\(u,v\)为根的子树,他......
  • 牛客练习赛130-A题题解
    牛客练习赛130-A题题解题目描述如下:给定两个整数x,y,jackle希望把x变成y。他每次可以进行如下两种操作之一:选择任意一个整数z,令x=x&z。选择任意一个整数z,令x=x|z。请问最少操作几次可以把x变成y。输入描述:本题有多组测试数据。第一行输入1个正整数T(1≤T......
  • [ABC134F] Permutation Oddness 题解
    T5[ABC134F]PermutationOddness很无敌的一道题。(好像是我第一次用无敌这个词把\(p_i\)和\(i\)的对应关系转化为球和盒子的配对问题,则原式中的绝对值顺利成章地就变成类似距离的一个东西。那么设\(f_{i,j,k}\)表示前\(i\)个球和盒子(注意球和盒子是一起考虑的,所以\(i......
  • [ARC185D] Random Walk on Tree 题解
    一个很套路的做法。思路题目要求走完整个树的时间,这并不好算,容易想到min-max容斥。依据min-max容斥,我们可以轻松把它转化成第一次走到所有子集的时间。考虑在这道题中,有什么特殊的。第一,任何包含根节点的子集答案都是零。第二,由于我们只关心第一次走到的点的时间,因此假......
  • ZZJC新生训练赛第五场题解
    A题思路题目要求构造一个相邻两项互质的长度为n的序列。可以直接选择输出从1到n的自然数序列,因为相邻的自然数总是互质的。题目有多个测试用例,因此需要逐个处理输入,并输出对应结果。代码#include<bits/stdc++.h>usingnamespacestd;intmain(){ios::sync_wit......
  • 题解:[YNOI2019] 游戏
    ProblemLink[YNOI2019]游戏题外话第一眼,由乃?不打不打。第二眼,欸noi三个字母怎么是大写(才发现是云南省选)。题意题意简洁,不再赘述。Solution一眼看出概率dp,但如何似乎没思路?开始公式做题:设置状态+推转移式。\(Q1\):怎么设置状态?首先,思考一个问题:第\(k\)个人该怎么“......
  • 异常问题解决
    异常:java程序编译或运行过程中出现的问题Throwable:Error:表示非常严重的问题,自己无法解决的问题Exception:除了RuntimeException其它异常【编译时期异常】:一般指的是异常尚未处理就编译了RuntimeException【运行时期异......
  • PTA L1系列题解(C语言)(L1_081 -- L1_088)
    L1-081今天我要赢题目内容:2018年我们曾经出过一题,是输出“2018我们要赢”。今年是2022年,你要输出的句子变成了“我要赢!就在今天!”然后以比赛当天的日期落款。输入格式:本题没有输入。输出格式:输出分2行。在第一行中输出I'mgonnawin!Today!,在第二行中用年年年......
  • P1955 程序自动分析 题解
    P1955程序自动分析一道并查集的裸题,并查集存储传递性,后判断。主题思路十分简单,重点在于离散化与离线的处理。离散化,为什么会想到离散化呢,观察数据范围\(1<i,j<{10}^9\),数据范围过大,导致数组不可能开到\(1e9\)但是\(1<n<1e5\)考虑到每次输入只有两个数,若每个数都两两不同,......
  • 【题解】[Codechef] Beautiful Permutation
    传送门以此纪念我场切的dp。这种计数的类型一看就很dp的样子。考场上一开始设的dp状态是\(dp_{i,j,k_1,k_2,0/1}\)表示将前\(i\)个数分为\(j\)段,放了\(k_1\)个偶数,\(k_2\)个奇数,当前段为偶数段或奇数段的方案数。考虑如何转移,记\(cnt_0\)表示序列中可填入的偶数......