线段树练习

题目
大意:给出一个长度为n的序列a,支持如下两个操作
1: 给[l,r]区间加上一个首项为val,公差为d的等差数列。
2: 询问[l,r]区间的和。
思路:利用线段树维护区间的首项(l处的值),公差。原题中还有一个取模:可能的模数m为25以内的素数,那么可以把所有数先对35。。1923取模,最后计算完再对m取模。
维护线段树时需要pushdown()函数,如果不用则要像区间加固定数一样记录祖先的影响,不太容易。同时查询时也要pushdown,因为影响是不可抵消的。
敲代码时还是得看一看屏幕,又在if语句后面多敲了个;,调了半天。

//please ac
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<set>
#include<queue>
#include<cstring>
#include<string>
#include<cmath>
#include<vector>
#include<stack>
#include<map>
using namespace std;
typedef long long ll;
const int maxn=4*2e5+5;
const ll mod=111546435;
ll sum[maxn],d[maxn],st[maxn];
int a[maxn/4];
int ql,qr;
void build_tree(int o,int l,int r)
{
    if(r<=l)
    {
        sum[o]=a[l]%mod;
        return;
    }
    int lc=2*o,rc=2*o+1,mid=l+(r-l)/2;
    build_tree(lc,l,mid);
    build_tree(rc,mid+1,r);
    sum[o]=(sum[lc]+sum[rc])%mod;
}
void maintain(int o,int l,int r)
{
    int lc=2*o,rc=2*o+1,len=r-l+1;
    if(l<r)
    {
        sum[o]=0;
        sum[o]=(sum[lc]+sum[rc])%mod;
    }
    else sum[o]=a[l];
    if(st[o]>=0)
    sum[o]+=len*st[o]%mod+1ll*len*(len-1)/2%mod*d[o]%mod;
    sum[o]%=mod;
}
void pushdown(int o,int l,int r)
{
    if(r<=l) return;
    int lc=2*o,rc=2*o+1;
    int mid=l+(r-l)/2;
    if(st[o]>=0)
    {
        if(st[lc]<0) st[lc]=0;
        if(st[rc]<0) st[rc]=0;
        st[lc]+=st[o];st[lc]%=mod;
        st[rc]+=(st[o]+(mid+1-l)*d[o]%mod)%mod;
        st[rc]%=mod;
        st[o]=-1;
        d[lc]=(d[lc]+d[o])%mod;
        d[rc]=(d[rc]+d[o])%mod;
        d[o]=0;
    }
}
void update(int o,int l,int r,int val,int c)
{
    int lc=2*o,rc=2*o+1;
    int mid=l+(r-l)/2;
    if(ql<=l&&qr>=r)
    {
        if(st[o]<0) st[o]=0;
        st[o]=((st[o]+val)%mod+1ll*(l-ql)*c%mod)%mod;
        d[o]=(d[o]+c)%mod;
    }
    else
    {
        pushdown(o,l,r);
        if(mid>=ql) update(lc,l,mid,val,c);
        else maintain(lc,l,mid);
        if(mid<qr) update(rc,mid+1,r,val,c);
        else maintain(rc,mid+1,r);
    }
    maintain(o,l,r);   
}
ll ans=0;
void query(int o,int l,int r)
{
    if(l>=ql&&r<=qr)
    {
        maintain(o,l,r);
        ans+=sum[o];
        ans%=mod;
        return;
    }
    int lc=2*o,rc=2*o+1;
    int mid=l+(r-l)/2;
    pushdown(o,l,r);
    if(mid>=ql) query(lc,l,mid);
    else maintain(lc,l,mid);
    if(qr>mid) query(rc,mid+1,r);
    else maintain(rc,mid+1,r);
    maintain(o,l,r);
}
int main()
{
    //freopen("tte.txt","r",stdin);
    memset(st,-1,sizeof(st));
    int n;scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        a[i]%=mod;
    }
    build_tree(1,1,n);
    int q;scanf("%d",&q);
    while(q--)
    {
        int op;scanf("%d",&op);
        if(op==1)
        {
            int val,c;
            scanf("%d%d%d%d",&ql,&qr,&val,&c);
            update(1,1,n,val,c);
        }
        else
        {
            int m;
            scanf("%d%d%d",&ql,&qr,&m);
            ans=0;
            query(1,1,n);
            printf("%lld\n",ans%m);
        }
        
    }
    return 0;
}                           
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值