首页 > 其他分享 >[lnsyoj281/luoguP2023/AHOI2009]维护序列

[lnsyoj281/luoguP2023/AHOI2009]维护序列

时间:2024-05-18 15:08:02浏览次数:27  
标签:int lnsyoj281 AHOI2009 luoguP2023 序列 cdots lazyadd 区间 lazytag

题意

原题链接
给定序列\(a\),要求维护区间加,区间乘,区间查询三种操作

sol

显然线段树,事实上,这是一道板子题(luoguP3373),但由于蒟蒻实在是太蒻了,并没有打过这道题。

区间加

如果我们将区间里的每一个元素都插入线段树做一次修改操作,那么一次修改操作的时间复杂度为\(O(n \log n)\),此时需要引入懒标记(lazytag)
当我们更新/查询到一个区间时,若这个区间的所有元素都在我们要更新/查询的区间中,即被其包含,那么我们就无需继续向下递归,而是在这个节点上记录一个lazytag。当下一次需要更新/查询其儿子时,再将lazytag向下传递
此时我们只需要处理lazytag即可
对于以下序列$$a_1, a_2, a_3, \cdots , a_n$$
此时这个序列的和为$$\sum_{i=1}^{n} a_i$$
此时,若将其中\([l, r]\)区间增加\(c\),则序列变为

\[a_1, a_2, a_3, \cdots , a_l + c, a_{l+1} + c, \cdots , a_{r} + c, \cdots, a_n \]

此时序列和变为$$\sum_{i=1}^{n} a_i + c(r - l + 1)$$
考虑到\(r-l+1\)可以处理出来,所以只需传递\(c\)即可

区间乘

类似于区间加,我们也可以使用lazytag进行操作
记两个lazytag,分别为\(lazymul\)和\(lazyadd\),来处理加法和乘法的情况
若将刚才的序列中的\([l, r]\)区间乘\(v\),则序列变为

\[a_1, a_2, a_3, \cdots , v(a_l + c), v(a_{l+1} + c), \cdots , v(a_{r} + c), \cdots, a_n \]

此时序列和变为$$\sum_{1\le i \le n, i \notin [l, r]}a_i + v\cdot \sum_{l\le i \le r} a_i + v\cdot c(r - l + 1)$$
我们发现,当执行区间乘操作时,不仅\(lazymul\)需要修改,而且\(lazyadd\)也需要乘以相应的数
最终,该区间的的和即为\(tr \cdot lazymul + lazyadd\)

代码

#include <iostream>
#include <algorithm>
#include <cstring>

using namespace std;
typedef long long LL;

const int N = 100005;

LL tree[N * 4];
LL lazyadd[N * 4], lazymul[N * 4];
int a[N];
int n, m, p;

void pushup(int u){
    tree[u] = tree[u << 1] + tree[u << 1 | 1];
}

void pushdown(int u, int l, int r){
    if (l != r){
        lazymul[u << 1] = (LL) lazymul[u << 1] * lazymul[u] % p;
        lazymul[u << 1 | 1] = (LL) lazymul[u << 1 | 1] * lazymul[u] % p;
        lazyadd[u << 1] = ((LL) lazyadd[u << 1] * lazymul[u] % p + lazyadd[u]) % p;
        lazyadd[u << 1 | 1] = ((LL) lazyadd[u << 1 | 1] * lazymul[u] % p + lazyadd[u]) % p; 
        int mid = l + r >> 1;
        tree[u << 1] = ((LL) tree[u << 1] * lazymul[u] % p + lazyadd[u] * (mid - l + 1)) % p;
        tree[u << 1 | 1] = ((LL) tree[u << 1 | 1] * lazymul[u] % p + lazyadd[u] * (r - mid)) % p;
    }
    lazymul[u] = 1, lazyadd[u] = 0;
}

void build(int u, int l, int r){
    lazymul[u] = 1, lazyadd[u] = 0;
    if (l == r) {
        tree[u] = a[l];
        return ;
    }
    int mid = l + r >> 1;
    build(u << 1, l, mid);
    build(u << 1 | 1, mid + 1, r);
    pushup(u);
}

void update(int u, int l, int r, int L, int R, LL val, int op){
    if (L <= l && r <= R){
        if (op) {
            lazyadd[u] = (lazyadd[u] + val) % p;
            tree[u] = (tree[u] + val * (r - l + 1)) % p;
        }
        else {
            lazymul[u] = (LL) lazymul[u] * val % p;
            lazyadd[u] = (LL) lazyadd[u] * val % p;
            tree[u] = (LL) tree[u] * val % p;
        }
        return ;
    }
    pushdown(u, l, r);
    int mid = l + r >> 1;
    if (L <= mid) update(u << 1, l, mid, L, R, val, op);
    if (R > mid) update(u << 1 | 1, mid + 1, r, L, R, val, op);
    pushup(u);
}

LL query(int u, int l, int r, int L, int R){
    if (L <= l && r <= R) return tree[u];
    pushdown(u, l, r);
    LL res = 0, mid = l + r >> 1;
    if (L <= mid) res = (res + query(u << 1, l, mid, L, R)) % p;
    if (R > mid) res = (res + query(u << 1 | 1, mid + 1, r, L, R)) % p;

    return res;
}

int main(){
    scanf("%d%d", &n, &p);
    for (int i = 1; i <= n; i ++ ) scanf("%d", &a[i]);
    build(1, 1, n);

    scanf("%d", &m);
    while (m -- ){
        int op;
        int t, g, c;
        scanf("%d", &op);
        if (op == 1){
            scanf("%d%d%d", &t, &g, &c);
            update(1, 1, n, t, g, c, 0);
        }
        else if (op == 2){
            scanf("%d%d%d", &t, &g, &c);
            update(1, 1, n, t, g, c, 1);
        }
        else {
            scanf("%d%d", &t, &g);
            printf("%lld\n", query(1, 1, n, t, g));
        }
    }

    return 0;
}

蒟蒻犯下的一些若至错误

  1. 建树的时候把a[l]写成了l,结果1h没调出来
  2. 取模没取尽,导致最终炸int

标签:int,lnsyoj281,AHOI2009,luoguP2023,序列,cdots,lazyadd,区间,lazytag
From: https://www.cnblogs.com/XiaoJuRuoUP/p/18199349/lnsyoj281_luoguP2023

相关文章

  • P2023 [AHOI2009] 维护序列
    原题链接code#definelllonglong#include<bits/stdc++.h>usingnamespacestd;lltree[410000]={0};llwait_mul[410000]={0};llwait_add[410000]={0};lln,p;inlinevoidread(ll&x){x=0;llflag=1;charc=getchar();while(c......
  • P2051 [AHOI2009] 中国象棋 题解
    DP。状态设计是点睛之笔。首先显然有每行或每列只能有至多\(2\)个棋子。设状态\(f_{i,j,k}\)为第\(i\)行,有\(j\)列只放了一个棋子,\(k\)列放了两个棋子。之后直接转移即可。注意边界判断。code:点击查看代码#include<bits/stdc++.h>#definelllonglong#defined......
  • P4126 [AHOI2009]最小割
    笔者最近思考了一种对反向边的最好的理解方法,即只是将其当成一个反悔机制,在残余网络中忽略掉所有的反向边。显然,仍然正确的捏。考虑跑完最小割后,一条边流满是其成为可行......
  • [AHOI2009] 中国象棋 题解
    每行每列的炮数量\(\leq2\),那么整个图就可以被分解为一些环和链。考虑答案的二元生成函数,显然环和链分别的生成函数都是平凡的多项式,而答案的多项式无非是加起来后求exp......
  • 【题解】P4126 [AHOI2009]最小割
    题意求最小割和可行边和必须边。思路清真,清真,还是**的清真。考虑可行边的充要条件:满流不存在另一条\(u,v\)间的最短路,即在残量网络上不存在包含\(u,v\)......
  • BZOJ 1797([Ahoi2009]Mincut 最小割-最小割的可行边与必行边)
    DescriptionA,B两个国家正在交战,其中A国的物资运输网中有N个中转站,M条单向道路。设其中第i(1≤i≤M)条道路连接了vi,ui两个中转站,那么中转站vi可以通过该道路到达ui中转站,......
  • NC19885 [AHOI2009]CHESS 中国象棋
    题目链接题目题目描述在N行M列的棋盘上,放若干个炮可以是0个,使得没有任何一个炮可以攻击另一个炮。请问有多少种放置方法,中国像棋中炮的行走方式大家应该很清楚吧.一个......