【线段树】BZOJ4373算术天才与等差数列

传送门
Description
给定一个长度为 n 的序列,其中第 i 个数为 ai。
有 m 次操作,每次要么修改其中的某一项,要么给出询问l; r; k,问区间 [l; r] 内的数从小到大排序后能否形成公差为 k 的等差数列。
Input
第一行包含两个正整数 n,m(1<=n,m<=300000) ,分别表示序列的长度和操作的次数。
第二行包含 n 个整数,依次表示序列中的每个数ai(0<=ai<=109)
接下来 m 行,每行一开始为一个数op
op=1 ,则接下来两个整数 x,y(1<=x<=n0<=y<=109) ,表示把 ax 修改为 y
op=2,则接下来三个整数 l,r,k(1<=l<=r<=n0<=k<=109) ,表示一个询问。
在本题中, x,y,l,r,k 都是经过加密的,都需要异或你之前输出的 Yes 的个数来进行解密。
Output
输出若干行,对于每个询问,如果可以形成等差数列,那么输出 Yes ,否则输出 No
Sample Input
5 3
1 3 2 5 6
2 1 5 1
1 5 4
2 1 5 1
Sample Output
No
Yes

条件一:这一区间的最大值与最小值的差满足等差数列的公式( aiaj=d(ij) )。
条件二:这一区间的所有数模 k 的值相同。
条件二相对于条件一麻烦得多,于是考虑相邻的两个数的差。如果差x k 的值为0,则满足。
这样就可以用线段树水过辣。
简单滴解释一下下面线段树中的标记含义。
maxv:区间最大值。minv:区间最小值。
l:左端点的值。r:右端点的值。
k:区间关于 k 成等差数列。
可能在写的过程中up函数比较难写。
注意 gcd(a,b) 中不能模 0 <script type="math/tex" id="MathJax-Element-44">0</script>。

据说可以用等差数列的和、各项平方的和水过 2333

#include <iostream>
#include <cstdio>
#include <algorithm>
#define MAXN 300005
using namespace std;

int n, m, num[MAXN];

inline int id(int x,int y){return (x+y)|(x!=y);}

int gcd(int a,int b)
{
    if(!a)return b;
    if(!b)return a;
    return gcd(b,a%b);
}

struct node
{
    int maxv, minv, l, r, k;
};

struct SGE
{
    node t[MAXN<<1];
    inline void up(node &a,node &b,node &c)
    {
        a.maxv=max(b.maxv,c.maxv), a.minv=min(b.minv,c.minv);
        a.l=b.l, a.r=c.r;
        a.k=gcd(gcd(b.k,c.k),abs(c.l-b.r));
    }
    void build(int code,int l,int r)
    {
        if(l==r)
        {
            t[code].maxv=t[code].minv=t[code].l=t[code].r=num[l];
            return;
        }
        int mid=(l+r)>>1;
        int lc=id(l,mid), rc=id(mid+1,r);
        build(lc,l,mid), build(rc,mid+1,r);
        up(t[code],t[lc],t[rc]);
    }
    node query(int code,int l,int r,int ql,int qr)
    {
        if(l==ql&&r==qr)return t[code];
        int mid=(l+r)>>1;
        if(qr<=mid)return query(id(l,mid),l,mid,ql,qr);
        if(mid<ql)return query(id(mid+1,qr),mid+1,r,ql,qr);
        int lc=id(l,mid), rc=id(mid+1,r);
        node ans, ans1, ans2;
        ans1=query(lc,l,mid,ql,mid), ans2=query(rc,mid+1,r,mid+1,qr);
        up(ans,ans1,ans2);
        return ans;
    }
    void change(int code,int l,int r,int pos,int d)
    {
        if(l==r)
        {
            t[code].maxv=t[code].minv=t[code].l=t[code].r=d;
            return;
        }
        int mid=(l+r)>>1;
        int lc=id(l,mid), rc=id(mid+1,r);
        if(pos<=mid)change(lc,l,mid,pos,d);
        else change(rc,mid+1,r,pos,d);
        up(t[code],t[lc],t[rc]);
    }
}sge;

bool solve(int l,int r,int k)
{
    if(l==r)return 1;
    node ans=sge.query(id(1,n),1,n,l,r);
    if(ans.k==k&&ans.maxv-ans.minv==k*(r-l))return 1;
    return 0;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i)scanf("%d",&num[i]);
    sge.build(id(1,n),1,n);
    int key=0, f, l, r, x;
    while(m--)
    {
        scanf("%d%d%d",&f,&l,&r);
        l^=key, r^=key;
        if(f==1)
            sge.change(id(1,n),1,n,l,r);
        else
        {
            scanf("%d",&x);
            x^=key;
            if(solve(l,r,x))
            {
                ++key;
                puts("Yes");
            }
            else puts("No");
        }
    }
    return 0;
}
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值