P4243 [JSOI2009]等差数列

P4243 [JSOI2009]等差数列
这个题可以用差分做
因为等差数列的性质就是差相等
我们维护这些东西
当前区间的左端点的数值 ->vl
当前区间的右端点的数值 -> vr
当前区间如果左右端点都不选有多少个等差数列 -> s[0]
当前区间如果只选左端点有多少个等差数列 -> s[1]
当前区间如果只选右端点有多少个等差数列 -> s[2]
当前区间如果左右端点都选有多少个等差数列 -> s[3]

就会发现
区间可以这么合并

vl[o]=vl[l];vr[o]=vr[r]; v l [ o ] = v l [ l ] ; v r [ o ] = v r [ r ] ;

s[o][0]=minn(s[l][1]+s[r][2](vr[l]==vl[r]),s[l][0]+s[r][2],s[r][0]+s[l][1]); s [ o ] [ 0 ] = m i n n ( s [ l ] [ 1 ] + s [ r ] [ 2 ] − ( v r [ l ] == v l [ r ] ) , s [ l ] [ 0 ] + s [ r ] [ 2 ] , s [ r ] [ 0 ] + s [ l ] [ 1 ] ) ;

s[o][1]=minn(s[l][1]+s[r][3](vr[l]==vl[r]),s[l][0]+s[r][3],s[r][1]+s[l][1]); s [ o ] [ 1 ] = m i n n ( s [ l ] [ 1 ] + s [ r ] [ 3 ] − ( v r [ l ] == v l [ r ] ) , s [ l ] [ 0 ] + s [ r ] [ 3 ] , s [ r ] [ 1 ] + s [ l ] [ 1 ] ) ;

s[o][2]=minn(s[l][3]+s[r][2](vr[l]==vl[r]),s[l][3]+s[r][0],s[r][2]+s[l][2]); s [ o ] [ 2 ] = m i n n ( s [ l ] [ 3 ] + s [ r ] [ 2 ] − ( v r [ l ] == v l [ r ] ) , s [ l ] [ 3 ] + s [ r ] [ 0 ] , s [ r ] [ 2 ] + s [ l ] [ 2 ] ) ;

s[o][3]=minn(s[l][3]+s[r][3](vr[l]==vl[r]),s[l][2]+s[r][3],s[r][1]+s[l][3]); s [ o ] [ 3 ] = m i n n ( s [ l ] [ 3 ] + s [ r ] [ 3 ] − ( v r [ l ] == v l [ r ] ) , s [ l ] [ 2 ] + s [ r ] [ 3 ] , s [ r ] [ 1 ] + s [ l ] [ 3 ] ) ;

就OK了
查询时要多开些点,保留合并的信息

#include<cstdio>
#include<iostream>
#define ls o<<1 
#define rs o<<1|1 
using namespace std;
const int M=1000000;
int n,q,a[M],cnt,maxn;
struct SIG{
    int vl[4*M],vr[4*M],s[4*M][4],add[4*M];
    int minn(int y,int z,int w){
        return min(min(w,y),z);
    }
    void update(int o,int l,int r){
        vl[o]=vl[l];vr[o]=vr[r];
        s[o][0]=minn(s[l][1]+s[r][2]-(vr[l]==vl[r]),s[l][0]+s[r][2],s[r][0]+s[l][1]);
        s[o][1]=minn(s[l][1]+s[r][3]-(vr[l]==vl[r]),s[l][0]+s[r][3],s[r][1]+s[l][1]);
        s[o][2]=minn(s[l][3]+s[r][2]-(vr[l]==vl[r]),s[l][3]+s[r][0],s[r][2]+s[l][2]);
        s[o][3]=minn(s[l][3]+s[r][3]-(vr[l]==vl[r]),s[l][2]+s[r][3],s[r][1]+s[l][3]);
    }
    void built(int o,int l,int r){
        if(l==r) {
            vl[o]=a[l];vr[o]=a[l];
            add[o]=0;s[o][1]=s[o][2]=s[o][3]=1;
            s[o][0]=0;return ;
        }
        int mid=(l+r)>>1;
        built(ls,l,mid);built(rs,mid+1,r);cnt=max(cnt,rs);maxn=cnt;
        add[o]=0;update(o,ls,rs);
    }
    void pushdown(int o){
        if(add[o]){
            add[ls]+=add[o];add[rs]+=add[o];
            vl[ls]+=add[o];vl[rs]+=add[o];
            vr[ls]+=add[o];vr[rs]+=add[o];
            add[o]=0;
        }
    }
    void modify(int o,int l,int r,int ql,int qr,int ins){
        if(l>qr||r<ql) return ;
        if(l>=ql&&r<=qr) {
            vl[o]+=ins;vr[o]+=ins;
            add[o]+=ins; return ;
        } 
        pushdown(o);int mid=(l+r)>>1;
        modify(ls,l,mid,ql,qr,ins);
        modify(rs,mid+1,r,ql,qr,ins);
        update(o,ls,rs);
    }
    int query(int o,int l,int r,int ql,int qr){
        if(l>qr||r<ql) return 0;
        if(l>=ql&&r<=qr)   return o;
        int mid=(l+r)>>1;pushdown(o);
        int t1=query(ls,l,mid,ql,qr);
        int t2=query(rs,mid+1,r,ql,qr);
        if(t1&&t2)update(++cnt,t1,t2);
        if(t1&&t2) return cnt;
        else return t1?t1:t2; 
    }
}T;
int main(){scanf("%d",&n);cnt=n;
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<n;i++) a[i]=a[i+1]-a[i];
    T.built(1,1,n-1);scanf("%d",&q);
    while(q--){char s[10];int l,r,x,y;
        scanf("%s%d%d",s,&l,&r);
        if(s[0]=='A'){scanf("%d%d",&x,&y);
            if(l!=1) T.modify(1,1,n-1,l-1,l-1,x);
            if(r!=n) T.modify(1,1,n-1,r,r,-(x+y*(r-l)));
            if(l!=r) T.modify(1,1,n-1,l,r-1,y);
        }
        else{if(l==r) puts("1");
            else cnt=maxn,printf("%d\n",T.s[T.query(1,1,n-1,l,r-1)][3]);
        }
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值