「JSOI2014」序列维护

「JSOI2014」序列维护

传送门
其实这题就是luogu的模板线段树2,之所以要发题解就是因为被 \(\color{black}{\text{M}} \color{red}{\text{_sea}}\) 告知了一种比较NB的 \(\text{update}\) 的方式。
我们可以把修改操作统一化,视为 \(ax + b\) 的形式,然后我们按照原来的套路来维护两个标记,分别代表 \(a\) 和 \(b\) ,那么我们的更新就可以这么写:

inline void f(int p, int atag, int mtag, int l, int r) {
    t[p].sum = (t[p].sum * mtag % P + 1ll * atag * (r - l + 1) % P) % P;
    t[p].atag = (t[p].atag * mtag + atag) % P;
    t[p].mtag = t[p].mtag * mtag % P;
}

然后我们就只需要写一个维护 \(ax + b\) 操作的修改就可以了。
其实我们还可以发现这个东西还可以用于区间赋值 \((a = 0)\)。
简直很妙有没有
参考代码:

#include <cstdio>
#define rg register
#define file(x) freopen(x".in", "r", stdin), freopen(x".out", "w", stdout)
typedef long long LL;
template < class T > inline void read(T& s) {
    s = 0; int f = 0; char c = getchar();
    while ('0' > c || c > '9') f |= c == '-', c = getchar();
    while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
    s = f ? -s : s;
}

const int _ = 100005;

int n, m; LL P, a[_];
struct node { LL sum, atag, mtag; } t[_ << 2];

inline int lc(int p) { return p << 1; }

inline int rc(int p) { return p << 1 | 1; }

inline void pushup(int p) { t[p].sum = (t[lc(p)].sum + t[rc(p)].sum) % P; }

inline void f(int p, int atag, int mtag, int l, int r) {
    t[p].sum = (t[p].sum * mtag % P + 1ll * atag * (r - l + 1) % P) % P;
    t[p].atag = (t[p].atag * mtag + atag) % P;
    t[p].mtag = t[p].mtag * mtag % P;
}

inline void pushdown(int p, int l, int r, int mid) {
    if (t[p].atag != 0 || t[p].mtag != 1) {
        f(lc(p), t[p].atag, t[p].mtag, l, mid);
        f(rc(p), t[p].atag, t[p].mtag, mid + 1, r);
        t[p].atag = 0, t[p].mtag = 1;
    }
}

inline void build(int p = 1, int l = 1, int r = n) {
    t[p].atag = 0, t[p].mtag = 1;
    if (l == r) { t[p].sum = a[l] % P; return ; }
    int mid = (l + r) >> 1;
    build(lc(p), l, mid), build(rc(p), mid + 1, r), pushup(p);
}

inline void update(int ql, int qr, LL atag, LL mtag, int p = 1, int l = 1, int r = n) {
    if (ql <= l && r <= qr) return f(p, atag, mtag, l, r);
    int mid = (l + r) >> 1;
    pushdown(p, l, r, mid);
    if (ql <= mid) update(ql, qr, atag, mtag, lc(p), l, mid);
    if (qr > mid) update(ql, qr, atag, mtag, rc(p), mid + 1, r);
    pushup(p);
}

inline LL query(int ql, int qr, int p = 1, int l = 1, int r = n) {
    if (ql <= l && r <= qr) return t[p].sum;
    int mid = (l + r) >> 1; LL res = 0;
    pushdown(p, l, r, mid);
    if (ql <= mid) res = (res + query(ql, qr, lc(p), l, mid)) % P;
    if (qr > mid) res = (res + query(ql, qr, rc(p), mid + 1, r)) % P;
    return res;
}

int main() {
#ifndef ONLINE_JUDGE
    file("cpp");
#endif
    read(n), read(P);
    for (rg int i = 1; i <= n; ++i) read(a[i]);
    build();
    read(m);
    for (rg int opt, l, r; m--; ) {
        read(opt); LL v;
        if (opt == 3) read(l), read(r), printf("%lld\n", query(l, r));
        else read(l), read(r), read(v), update(l, r, opt != 2 ? 0 : v, opt != 1 ? 1 : v);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/zsbzsb/p/12249903.html

时间: 2024-10-08 20:44:47

「JSOI2014」序列维护的相关文章

「JSOI2014」矩形并

「JSOI2014」矩形并 传送门 我们首先考虑怎么算这个期望比较好. 我们不难发现每一个矩形要和 \(n - 1\) 个矩形去交,而总共又有 \(n\) 个矩形,所以我们把矩形两两之间的交全部加起来再除以 \(n(n - 1)\) 就是答案. 至于算矩形之间的交我们可以考虑把每个矩形都视为在这个矩形范围内区间加上 \(1\) ,那么我们只需要查询一个矩形内的和 - 该矩形自身的贡献就可以算出一个矩形与其他矩形的交. 所以现在相当于我们只需要实现二维的区间加/查询. 但是数据范围很大我们不可能用

AC日记——「SDOI2017」序列计数 LibreOJ 2002

「SDOI2017」序列计数 思路: 矩阵快速幂: 代码: #include <bits/stdc++.h> using namespace std; #define mod 20170408 #define ll long long struct MatrixType { int n,m; ll ai[105][105]; void mem(int n_,int m_) { n=n_,m=m_; for(int i=0;i<=n;i++) for(int v=0;v<=m;v++

LibreOJ #2002. 「SDOI2017」序列计数

二次联通门 : LibreOJ #2002. 「SDOI2017」序列计数 /* LibreOJ #2002. 「SDOI2017」序列计数 线性筛 + 矩阵优化dp 先构造出全部情况的矩阵 用矩阵快速幂计算答案 再构造出全不是质数的矩阵 计算出答案 前一个答案减后一个答案即可 */ #include <cstdio> #include <iostream> #include <cstring> const int BUF = 12312312; char Buf[BU

「JSOI2014」强连通图

「JSOI2014」强连通图 传送门 第一问很显然就是最大的强连通分量的大小. 对于第二问,我们先把原图进行缩点,得到 \(\text{DAG}\) 后,统计出入度为零的点的个数和出度为零的点的个数,两者取 \(\max\) 就是答案. 理性证明可以看这里 参考代码: #include <cstdio> #define rg register #define file(x) freopen(x".in", "r", stdin), freopen(x&q

「JSOI2014」学生选课

「JSOI2014」学生选课 传送门 看到这题首先可以二分. 考虑对于当前的 \(mid\) 如何 \(\text{check}\) 我们用 \(f_{i,j}\) 来表示 \(i\) 对 \(j\) 的好感度排名,那么对于两个人 \(i\),\(j\) 如果有 \(\max\{f_{i, j}, f_{j, i}\} > mid\) 那么显然这两个人是不能上同一个老师的课的. 而且每个人可以上的课只有两种,我们记为 \(a_{i, 0 / 1}\) 假设 \(i\),\(j\) 对于当前的 \

「JSOI2014」打兔子

「JSOI2014」打兔子 传送门 首先要特判 \(k \ge \lceil \frac{n}{2} \rceil\) 的情况,因为此时显然可以消灭所有的兔子,也就是再环上隔一个点打一枪. 但是我们又会发现当 \(n = 3, k = 2\) 时,这种情况也满足上述条件但是我们只能打掉两群兔子,所以选兔子最多的两个格子打. 对于剩下的情况我们可以考虑 \(\text{DP}\) . 我们可以发现一件事,就是说如果我们把环弱化成链,那么顺着打就可以包含所有状态了. 所以说我们就可以有一个性质:两个

「LuoguP1430」 序列取数

题目描述 给定一个长为n的整数序列(n<=1000),由A和B轮流取数(A先取).每个人可从序列的左端或右端取若干个数(至少一个),但不能两端都取.所有数都被取走后,两人分别统计所取数的和作为各自的得分.假设A和B都足够聪明,都使自己得分尽量高,求A的最终得分. 输入输出格式 输入格式: 第一行,一个正整数T,表示有T组数据.(T<=100) 接着T行,每行第一个数为n,接着n个整数表示给定的序列. 输出格式: 输出T行,每行一个整数,表示A的得分 输入输出样例 输入样例#1: 复制 2 1

loj2051 「HNOI2016」序列

ref #include <algorithm> #include <iostream> #include <cstdio> #include <cmath> using namespace std; typedef long long ll; int n, q, a[100005], l[100005], r[100005], sta[100005], din, blc, bel[100005], st[100005][19], mii[17], mlg[

loj#2002. 「SDOI2017」序列计数(dp 矩阵乘法)

题意 题目链接 Sol 质数的限制并没有什么卵用,直接容斥一下:答案 = 忽略质数总的方案 - 没有质数的方案 那么直接dp,设\(f[i][j]\)表示到第i个位置,当前和为j的方案数 \(f[i + 1][(j + k) \% p] += f[i][j]\) 矩乘优化一下. #include<bits/stdc++.h> #define LL long long using namespace std; const int MAXN = 2e7 + 10, mod = 20170408,