[JSOI 2009] 等差数列

题目描述:

给出一串序列
有两个操作
A S T a b 表示 S T区间内每个数 Vi 变成 Vi+(i-s)*b+a
B S T 表示 查询 S T 区间内的数最少可以划分为几段等差数列

题目分析:

线段树玩出花系列~
首先,对于等差数列
我们其实并不需要真实的值
而是前后两个元素的差值
一段 等差数列 差分之后 值一定是相等的
那么我们就把序列差分 Val[i]=Val[i+1]-Val[i]
对 差分值建立线段树
对于操作 1
差分之后等价于
在 S 的位置 +a
而在 T -((a+b*(t-s))
在 S - T-1的位置加上 b
如何统计答案呢
对于每个线段树区间维护 l,r s[0],s[1],s[2],s[3]
l,r 分别表示区间右端和左端的值
s[0] 左右端点都不选的划分数,s[1] 选上左端点的划分数,s[2] 选上右端点的划分数,s[3] 左右都选的划分数
以s[0]为例 s[0]=min(lc->s[2]+rc->s[1]-(lc->r==rc->l), lc->s[0]+rc->s[1], lc->s[2]+rc->s[0])
其他依次类推
注意特判和细节!

题目链接:

Luogu 4242
BZOJ 1558

Ac 代码:

#include <cstdio>
#include <iostream>
#define il inline
#define lson (o<<1)
#define rson (o<<1)|1
il int read()
{
    int x=0,w=1;
    char ch=0;
    while(ch<'0'||ch>'9')
    {
        if(ch=='-') w=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        x=(x<<3)+(x<<1)+ch-'0';
        ch=getchar();
    }
    return x*w;
}
const int maxm=110005;
il int min(int a,int b,int c)
{
    return std::min(std::min(a,b),c);
}
struct node{
    int l,r,s[4];//s[0] 左右端点都不选的划分数,s[1] 选上左端点的划分数,s[2] 选上右端点的划分数,s[3] 左右都选的划分数 
    node operator + (const node &a) const
    {
        node tmp;
        tmp.l=l; tmp.r=a.r;
        tmp.s[0]=min(s[2]+a.s[1]-(r==a.l),s[0]+a.s[1],s[2]+a.s[0]);
        tmp.s[1]=min(s[3]+a.s[1]-(r==a.l),s[1]+a.s[1],s[3]+a.s[0]);
        tmp.s[2]=min(s[2]+a.s[3]-(r==a.l),s[0]+a.s[3],s[2]+a.s[2]);
        tmp.s[3]=min(s[3]+a.s[3]-(r==a.l),s[1]+a.s[3],s[3]+a.s[2]);
        return tmp;
    }
};
struct tree{
    int add;
    node t;
}st[maxm<<2]; 
int val[maxm],n,q;
namespace seg{
    il node xclear()
    {
        node x;
        x.l=x.r=x.s[0]=x.s[1]=x.s[2]=x.s[3]=0;
        return x;
    }
    void build(int o,int l,int r)
    {
        if(l==r)
        {
          st[o].t.s[0]=0;
          st[o].t.s[1]=st[o].t.s[2]=st[o].t.s[3]=1;
          st[o].t.l=st[o].t.r=val[l];
          st[o].add=0;  
          return;
        }
        int mid=(l+r)>>1;
        build(lson,l,mid),build(rson,mid+1,r);
        st[o].add=0;
        st[o].t=st[(o<<1)].t+st[(o<<1)|1].t;
    }
    il void col(int o,int adx)
    {
        st[o].add+=adx;
        st[o].t.l+=adx,st[o].t.r+=adx;
    }
    il void pushdown(int o)
    {
        if(st[o].add)
        {
            col(lson,st[o].add);
            col(rson,st[o].add);
            st[o].add=0;
        }
    }
    void change(int o,int l,int r,int ql,int qr,int num) 
    {
        if(ql<=l&&r<=qr)
        {
            col(o,num);
            return;
        }
        pushdown(o);
        int mid=(l+r)>>1;
        if (ql<=mid) change(lson,l,mid,ql,qr,num);
        if (qr>mid) change(rson,mid+1,r,ql,qr,num);
        st[o].t=st[(o<<1)].t+st[(o<<1)|1].t;
    }
    node ask(int o,int l,int r,int ql,int qr)
    {
        if(ql<=l&&r<=qr) return st[o].t;
        pushdown(o); 
        int mid=(l+r)>>1;
        if(qr<=mid) return ask(lson,l,mid,ql,qr);
        if(ql>mid) return ask(rson,mid+1,r,ql,qr);
        return ask(lson,l,mid,ql,qr)+ask(rson,mid+1,r,ql,qr);
    }
}
int main()
{
    //freopen("test.in","r",stdin);
    n=read();
    for(int i=1;i<=n;i++) val[i]=read();
    for(int i=1;i<n;i++) val[i]=val[i+1]-val[i];
    //for(int i=1;i<n;i++) printf("%d ",val[i]);
    seg::build(1,1,n-1);
    q=read();
    while(q--)
    {
        char s[10];
        scanf("%s",s);
        if(s[0]=='A')
        {
            int u=read(),v=read(),a=read(),b=read();
            if(u!=1) seg::change(1,1,n-1,u-1,u-1,a);
            if(v!=n) seg::change(1,1,n-1,v,v,-(a+b*(v-u)));
            if(u!=v) seg::change(1,1,n-1,u,v-1,b);
        }
        else
        {
            int u=read(),v=read();
            if(u==v) printf("1\n");
            else printf("%d\n",seg::ask(1,1,n-1,u,v-1).s[3]);
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值