题解 P1438 【无聊的数列】

· · 题解

遇事不决先分块

分块——一种优雅的暴力,既可以感受到暴力的美感,也可以享受AC的快感

这道题其实没啥,只要学过数列就行;发现数列的首项与公差是可以相加的

  例如 1 2 3 4  5  6 首项是1,公差为1
       1 3 5 7  9  11 首项是1,公差为2
       2 5 8 11 14 17 上下相加即为首项为2,公差为3的新数列

重点维护:

数列最要的首项和公差;对于每个块都维护这样两个值,查询时O(1)推一下就行

最后奉上代码:

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#define N 300500
using namespace std;

inline int read() {
    int x = 0,f = 1; char s = getchar();
    while(s < '0' || s > '9') {if(s == '-') f = -1;s = getchar();}
    while(s >= '0' && s <= '9') {x = x * 10 + s - '0';s = getchar();}
    return f * x;
}

int n,m;
int a[N];
int tag[N],L[N],R[N];
int lzk[N],lzd[N];

inline void Change(int l,int r,int k,int d) {
    if(tag[l] == tag[r]) {
        for(int i = l;i <= r;i ++)
            a[i] += k + d * (i - l);
        return ;
    }
    int res = k + d * (R[tag[l]] - l + 1);
    for(int i = l;i <= R[tag[l]];i ++) 
        a[i] += k + d * (i - l);
    for(int i = tag[l] + 1;i < tag[r];i ++)
        lzk[i] += res,lzd[i] += d,res += d * (R[i] - L[i] + 1);
    for(int i = L[tag[r]];i <= r;i ++)
        a[i] += res + d * (i - L[tag[r]]);
}

inline int Ask(int p) {
    int k = lzk[tag[p]],d = lzd[tag[p]];
    int res = a[p] + k + d * (p - L[tag[p]]);
    return res;
}

int main() {
    n = read(),m = read(); int len = sqrt(n);
    for(int i = 1;i <= n;i ++) a[i] = read();
    for(int i = 1;i <= n;i ++) tag[i] = (i - 1) / len + 1;
    for(int i = 1;i <= tag[n];i ++) 
        L[i] = R[i - 1] + 1,R[i] = min(n,L[i] + len - 1);
    for(int i = 1;i <= m;i ++) {
        int opt = read();
        if(opt == 1) {
            int l = read(),r = read(),k = read(),d = read();
            Change(l,r,k,d);
        }
        else {
            int p = read();
            printf("%d\n",Ask(p));
        }
    }
}