线段树-洛谷P1438 无聊的数列

https://www.luogu.org/problem/show?pid=1438
一开始吓死,给区间加上一个等差数列???
然后就发现我们只要维护题目里说的k,d两个之就好了;
比如我读入是1,x,y,k,d;
那么对于在x~y这个区间里面的小区间l~r
小区间的k,d值相应就是
k’=k+d*(l-x);
d’=d;
这个很显然把;
换句话说,x~y变成一个等差序列,那么对于i小区间l~r显然也是一个等差序列;
那么直接线段树好了;
比什么毒瘤数据题好多了;
然后洛谷数据有毒;

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
using namespace std;
struct tree{
    int l,r,k,d;
}T[262144];
int a[100005];
int n,m,x,y,z,k,d;
void maketree(int l,int r,int num){
    T[num].l=l; T[num].r=r;
    if(l==r)return;
    int mid=l+r>>1; num=num<<1;
    maketree(l,mid  ,num  );
    maketree(mid+1,r,num+1);
}
void init(int num){
    if(x<=T[num].l&&T[num].r<=y){
        T[num].k+=k+d*(T[num].l-x);
        T[num].d+=d;
        return;
    }
    num=num<<1;
    if(T[num  ].r>=x)init(num  );
    if(T[num+1].l<=y)init(num+1);
}
int outit(int num){
    if(T[num].l==T[num].r)
        return a[T[num].l]+T[num].k;
    int ans=T[num].k+T[num].d*(x-T[num].l);
    num=num<<1;
    if(T[num].r>=x)return ans+outit(num);
    else return ans+outit(num+1);
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)scanf("%d",&a[i]);
    maketree(1,n,1);
    while(m--){
        scanf("%d",&z);//这里一定要用一个新的变量 
        if(z==1){
            scanf("%d%d%d%d",&x,&y,&k,&d);
            init(1);
        }else{
            scanf("%d",&x);
            printf("%d\n",outit(1));
        }
    }
}

洛谷题解还有一个非常妙的思路;

实际上可以维护一个差分数组,不需要进行复杂的计算
只需要写一个很普通的滋磁区间加法和区间求和的线段树就可以了
1操作可以转化为如下操作:
将a[L]在差分数组中的对应值d[L]加上K
将a[L+1]至a[R]在差分数组中的对应值加上D
将a[R+1]对应的d[R+1]减去K+D*(R-L),即减去数列的末项
2操作即在差分数组中求d[1]至d[P]的和

posted @ 2017-04-07 09:38  largecube233  阅读(121)  评论(0编辑  收藏  举报