洛谷-P1438 无聊的数列

P1438 无聊的数列
题目背景
无聊的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个数的值a[P]。

输入格式:
第一行两个整数数n,m,表示数列长度和操作个数。

第二行n个整数,第i个数表示a[i](i=1,2,3…,n)。

接下来的m行,表示m个操作,有两种形式:

1 L R K D

2 P 字母意义见描述(L≤R)。

输出格式:
对于每个询问,输出答案,每个答案占一行。

输入样例:
5 2
1 2 3 4 5
1 2 4 1 2
2 3
输出样例:
6
说明
数据规模:

0≤n,m≤100000

|a[i]|,|K|,|D|≤200

Hint:

有没有巧妙的做法?

题解
作者比较菜,不知道到什么是差分……
但是发现等差数列扣去K,那么剩下的就是0,D,2D,3D……
这个东西让我想到了前缀和,所以,对于 L 到 R 段的修正,分成两部分。
第一部分是前面的 K。
第二个部分是0,D,2D,3D……

第一部分直接用树状数组维护(详解部分,懂的跳过。在L的位置增加D,R+1的位置 -D 再刷趟前缀和,就会发现L到R这段都修正成了D)
第二部分,我们可以用线段树维护(详解部分,懂的跳过。与前面同理,L+1到R每个增加D,刷前缀和,L到R这段就是…区间修改就想到线段树,详见代码)

当然,洛谷博客里也有很多别的解法和写得更好的代码传送门
代码

#include<cstdio>
#define LL long long
const int maxn=100005;
int n,m,rot,tot;
struct nod{
    int L,R;//表示左右儿子 
    LL sum,laz;//表示字数权值和 和 懒惰标记 
    nod(){L=R=0;sum=laz=0;}
}a[maxn<<2];
LL c[maxn];
int read()
{
    int ret=0,f=1;char ch=getchar();
    while (ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=getchar();}
    while (ch>='0'&&ch<='9') ret=ret*10+ch-'0',ch=getchar();
    return ret*f;
}
void add(int x,int y){while(x<=n)c[x]+=y,x+=x&-x;}
LL get(int x){LL s=0; while(x)s+=c[x],x-=x&-x;return s;}
int build(int L,int R)//建树 
{
    int now=++tot;
    if (L==R) return now;
    int mid=L+R>>1;
    a[now].L=build(L,mid);
    a[now].R=build(mid+1,R);
    a[now].sum=a[a[now].L].sum+a[a[now].R].sum;
    return now;
}
void pushdown(nod &x,int L,int R)//lazy标记下推 
{
    int son,mid=L+R>>1;LL &laz=x.laz;
    if (son=x.L) a[son].laz+=laz,a[son].sum+=laz*(mid-L+1);
    if (son=x.R) a[son].laz+=laz,a[son].sum+=laz*(R-mid);
    laz=0;
}
void addtre(int x,int L,int R,int le,int ri,int y)//修正某段权值 
{
    if (L<=le&&ri<=R){a[x].laz+=y;a[x].sum+=y*(ri-le+1);return;}
    pushdown(a[x],le,ri);
    if (ri<L||le>R) return;
    int mid=le+ri>>1;
    if (L<=mid) addtre(a[x].L,L,R,le,mid,y);
    if (mid+1<=R) addtre(a[x].R,L,R,mid+1,ri,y);
    a[x].sum=a[a[x].L].sum+a[a[x].R].sum;
}
LL gettre(int x,int L,int R,int le,int ri)//查找某段权值 
{
    pushdown(a[x],le,ri);
    if (L<=le&&ri<=R){return a[x].sum;}
    if (ri<R||le>R) return 0;
    int mid=le+ri>>1;LL sum=0;
    if (L<=mid) sum+=gettre(a[x].L,L,R,le,mid);
    if (mid+1<=R) sum+=gettre(a[x].R,L,R,mid+1,ri);
    return sum;
}
int main()
{
    n=read();m=read();
    for (int i=1,x;i<=n;i++) x=read(),add(i,x),add(i+1,-x);
    ++n;
    rot=build(1,n);
    for (int i=1;i<=m;i++)
    {
        if (read()==1)
        {
            int L=read(),R=read(),k=read(),d=read();
            add(L,k);add(R+1,-k);
            if (L==R||!d) continue;
            addtre(rot,L+1,R,1,n,d);
            addtre(rot,R+1,R+1,1,n,-d*(R-L));
        }else
        {
            int x=read();
            printf("%lld\n",get(x)+gettre(rot,1,x,1,n));
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xu0_zy/article/details/80286360
今日推荐