[LuoguP1438]无聊的数列(差分+线段树/树状数组)

\(Link\)

\(\mathcal{Description}\)

给你一个数列,要求支持单点查询\(and\)区间加等差数列。

\(\mathcal{Solution}\)

哈哈哈哈这个题十分的有意思,至于为什么有意思等会儿再说~

其实我们观察这两个操作,单点查询……就是那个\(naive\)的单点查询,那么区间加等差数列呢?我们可以思考一下等差数列的性质——存在公差。不妨考虑差分

\(emmm\)发现我好像还没有在博客园里提过差分……那么就整一整吧正好我好久没捯饬这玩意儿了\(qwq\)

差分

其实就是对于一个给定的数列\(base\),我们用另一个数组\(dif_i\)记录\(base_i - base_{i - 1}\),从而我们可以通过\(dif\)反向得到\[base_i = \sum_{j = 1}^{i}{dif_j}\]呐,我们如果有区间加减这种操作或者其他的,我们可以通过操作\(dif_i\)和\(dif_{j + 1}\)来起到对区间\(i\)~\(j\)打标记的作用。关键就是一定要是单点查询……区间查询仿佛也可以做?但是有点麻烦略略略。

回到这个题,我们的线段树可以建在数列的差分数组上。然后区间加等差数列的时候,我们就让\(dif_L += D\),\(dif_{L+1...R} += K\),\(dif_{R+1} -= (K \times (R - L) + D)\)很显然。如果要是区间查询的话,我们就直接线段树求个\[ans = \sum_{i = 1}^{P}{dif_i}\]但是在程序实现的时候,笔者在此偷了个懒,没有初始化\(dif\)数组,那么我们就需要在区间查询的时候改成这样\[ans = \sum_{i = 1}^{P}{dif_i} + base_P\]

\(Code\)

#include <cstdio>
#include <iostream>
#define mid ((l + r) >> 1)

using namespace std ;
const int MAXN = 100050 ;
int N, M, P, mark, i, base[MAXN] ;
int L, R, K, D, dif[MAXN << 2], tag[MAXN << 2] ;

inline int qrd(){
    int k = 0, f = 1 ; char c = getchar() ;
    while(!isdigit(c)) {if(c == ‘-‘) f = -1; c = getchar() ;}
    while(isdigit(c)) k = (k << 1) + (k << 3) + c - 48, c = getchar() ;
    return k * f ;
}
inline void p_u(int rt){dif[rt] = dif[rt << 1] + dif[rt << 1 | 1] ;}
inline void p_d(int rt, int l, int r){
    if(tag[rt]){
        dif[rt << 1] += tag[rt] * (mid - l + 1) ;
        dif[rt << 1 | 1] += tag[rt] * (r - mid) ;
        tag[rt << 1] += tag[rt] ;
        tag[rt << 1 | 1] += tag[rt] ;
        tag[rt] = 0 ;
    }
}
void update(int rt, int l, int r, int ul, int ur, int k){
    if(ul <= l && r <= ur){
        tag[rt] += k ;
        dif[rt] += k * (r - l + 1) ;
        return ;
    }p_d(rt, l, r) ;
    if(ul <= mid) update(rt << 1, l, mid, ul, ur, k) ;
    if(ur > mid) update(rt << 1 | 1, mid + 1, r, ul, ur, k) ;
    p_u(rt) ;
}
int query(int rt, int l, int r, int ql, int qr){
    if(ql <= l && r <= qr){return dif[rt] ;}p_d(rt, l, r) ;
    int res = 0 ;
    if(ql <= mid) res += query(rt << 1, l, mid, ql, qr) ;
    if(qr > mid) res += query(rt << 1 | 1, mid + 1, r, ql, qr) ;
    return res ;
}
int main(){
    N = qrd(), M = qrd() ;
    for(i = 1; i <= N; i ++) base[i] = qrd() ;
    for(i = 1; i <= M; i ++){
        cin >> mark ;
        if (mark == 1) {
            L = qrd(), R = qrd(), K = qrd(), D = qrd() ;
            update(1, 1, N, L, L, K) ;
            if (R > L) update(1, 1, N, L + 1, R, D) ;
            if (R != N) update(1, 1, N, R + 1, R + 1, -(R - L) * D - K) ;
        }
        else {
            P = qrd() ;
            cout << base[P] + query(1, 1, N, 1, P) << endl ;
        }
    }
}

原文地址:https://www.cnblogs.com/pks-t/p/9352195.html

时间: 2024-12-12 22:39:52

[LuoguP1438]无聊的数列(差分+线段树/树状数组)的相关文章

无聊的数列【线段树】

题目背景 无聊的YYB总喜欢搞出一些正常人无法搞出的东西.有一天,无聊的YYB想出了一道无聊的题:无聊的数列...(K峰:这题不是傻X题吗) 题目描述 维护一个数列{a[i]},支持两种操作: 1.1 L R K D:给出一个长度等于R-L+1的等差数列,首项为K,公差为D,并将它对应加到a[L]~a[R]的每一个数上.即:令a[L]=a[L]+K,a[L+1]=a[L+1]+K+D, a[L+2]=a[L+2]+K+2D--a[R]=a[R]+K+(R-L)D. 2.2 P:询问序列的第P个数

线段树&amp;数状数组

线段树 单点修改,区间查询 #include<bits/stdc++.h> using namespace std; int n,q; long long num[1000010]; struct tree { int l,r; long long sum,max; }t[4000010]; void BuildTree(int,int,int); void Update(int,int,int,int,long long); long long Query(int,int,int,int,i

差分数列+树状数组

差分数列+树状数组:可以把树状数组的“单点修改,区间查询”-->改变为“区间修改和单点查询” 例题: codevs 1081 线段树练习 2 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 大师 Master 题目描述 Description 给你N个数,有两种操作 1:给区间[a,b]的所有数都增加X 2:询问第i个数是什么? 输入描述 Input Description 第一行一个正整数n,接下来n行n个整数,再接下来一个正整数Q,表示操作的个数. 接下来Q行每行若干个整数

【线段树】【树状数组】【CF 121E】幸运数列

1922. [CF 121E]幸运数列 ★★★ 输入文件:cf121e.in 输出文件:cf121e.out 简单对比 时间限制:3 s 内存限制:256 MB [题目描述] 对于欧洲人来说,"幸运数"是指那些十进制只由4或7组成的数.财务员Petya需要维护一个支持如下操作的整数数列: add l r d - 表示将[l, r]区间内的所有数加上一个正整数d(). count l r - 统计[l, r]区间内有多少个"幸运数".() 请你帮助Petya实现它.

bzoj1789 AHOI 维护数列(线段树)

首先想到线段树,然后刚开始写忽然想到树状数组求和岂不是更快,而且编程复杂度又小,于是把之前写的删掉,写树状数组,写完模版之后忽然发现这题竟然是区间修改! 于是又删掉重写,忽然发现不会处理又加又乘的,果断看题解-- 经过几乎两个小时的调试,终于1A. 需要注意的是,一定要让线段树的每一个区间保存的值时刻为正确的,这样才能在调用时直接返回.比如说这道题的change和query操作的最后一句话: sum:=f(g[k<<1]+g[k<<1+1]) 而不是 sum:=f(t[k<&

COGS 2638. 数列操作ψ 线段树

传送门 : COGS 2638. 数列操作ψ 线段树 这道题让我们维护区间最大值,以及维护区间and,or一个数 我们考虑用线段树进行维护,这时候我们就要用到吉司机线段树啦 QAQ 由于发现若干次and,or之后,如果数据分布均匀,那么几乎所有的数在若干次操作后都会变成同一个数 因为我们的and操作中的0位,以及or操作当中的1位,都是可以把整个区间的那一二进制位重置为相同的 我们考虑利用这一个性质 如果我们直接维护一个区间内的值是否是相同的,那么效果会差很多. 我们发现我们在进行and操作的时

ZZNU-OJ-2098 : Drink coffee【线段树合并区间或者 差分 + 二分索引树】

1 2098 : Drink coffee 2 时间限制:1 Sec 内存限制:256 MiB 3 提交:40 答案正确:14 4 5 提交 状态 讨论区 6 7 题目描述 8 为了在上课时保持清醒,凯伦需要一些咖啡.咖啡爱好者凯伦想知道最佳的温度来冲煮完美的咖啡.因此,她花了一些时间阅读几本食谱,其中包括广受好评的“咖啡的艺术”. 9 她知道有n个食谱,其中第i个食谱建议应当在li和ri度之间冲煮以达到最佳的味道.凯伦认为如果至少k个食谱推荐某个温度,那么那个温度是可以接受的. 10 凯伦的性

UVA 11990 ”Dynamic“ Inversion(线段树+树状数组)

[题目链接] UVA11990 [题目大意] 给出一个数列,每次删去一个数,求一个数删去之前整个数列的逆序对数. [题解] 一开始可以用树状数组统计出现的逆序对数量 对于每个删去的数,我们可以用线段树求出它在原序列中的逆序对贡献 在线段树的每个区间有序化数据,就可以二分查找出这个数在每个区间的位置, 这样就处理出了划分出的区间的贡献,先用答案减去这一部分 接下来考虑已经删去部分的容斥,我们发现只要对删去部分再做一次类似的操作, 将这一部分跟当前删去数求一次贡献就是刚才多减去的部分,将这部分的答案

【树状数组区间修改区间求和】codevs 1082 线段树练习 3

http://codevs.cn/problem/1082/ [AC] 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn=2e5+2; 5 int n; 6 ll a[maxn]; 7 ll c1[maxn]; 8 ll c2[maxn]; 9 int lowbit(int x) 10 { 11 return x&-x; 12 } 13 void add(l