【BZOJ4373】算术天才⑨与等差数列

Description

算术天才⑨非常喜欢和等差数列玩耍。
有一天,他给了你一个长度为n的序列,其中第i个数为a[i]。
他想考考你,每次他会给出询问l,r,k,问区间[l,r]内的数从小到大排序后能否形成公差为k的等差数列。
当然,他还会不断修改其中的某一项。
为了不被他鄙视,你必须要快速并正确地回答完所有问题。
注意:只有一个数的数列也是等差数列。

Input

第一行包含两个正整数n,m(1<=n,m<=300000),分别表示序列的长度和操作的次数。
第二行包含n个整数,依次表示序列中的每个数ai
接下来m行,每行一开始为一个数op,
若op=1,则接下来两个整数x,y(1<=x<=n,0<=y<=10^9),表示把a[x]修改为y。
若op=2,则接下来三个整数l,r,k(1<=l<=r<=n,0<=k<=10^9),表示一个询问。
在本题中,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
HINT

Source

如果l,r是公差为k的等差数列,一定满足两个条件:
1. maxri=la[i]minri=l=(rl)k
2.序列里相邻两个数的差的gcd为k
这是蛮显然的(虽然根本没试过证明
然后直接线段树
题解发出5分钟后的UPD>
必须满足区间里不可重…但是我懒得加了..加上代码就上3KB辣
看题解的人懂就好_ (:зゝ∠)_

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define lchild rt<<1,l,mid
#define rchild rt<<1|1,mid+1,r
#define ln rt<<1
#define rn rt<<1|1
#define GET (ch>='0'&&ch<='9')
#define MAXN 300010
using namespace std;
int n,m,cnt;
int a[MAXN];
void in(int &x)
{
    char ch=getchar();x=0;
    while (!GET)    ch=getchar();
    while (GET) x=x*10+ch-'0',ch=getchar();
}
int gcd(int a,int b)    {return !b?a:gcd(b,a%b);}
int Abs(int x)  {return x>0?x:-x;}
struct seg
{
    int l,r,maxn,minn,Gcd,lnum,rnum;
}tree[MAXN<<2],empty;
void push_up(int rt)
{
    tree[rt].lnum=tree[ln].lnum;tree[rt].rnum=tree[rn].rnum;
    tree[rt].maxn=max(tree[ln].maxn,tree[rn].maxn);tree[rt].minn=min(tree[ln].minn,tree[rn].minn);
    tree[rt].Gcd=gcd(gcd(tree[ln].Gcd,tree[rn].Gcd),Abs(tree[ln].rnum-tree[rn].lnum));
}
void build(int rt=1,int l=1,int r=n)
{
    tree[rt].l=l;tree[rt].r=r;
    if (l==r)   {tree[rt].maxn=tree[rt].minn=tree[rt].lnum=tree[rt].rnum=a[l];return;}
    int mid=(l+r)>>1;build(lchild);build(rchild);push_up(rt);
}
void modify(int rt,int x,int delta)
{
    int L=tree[rt].l,R=tree[rt].r,mid=(L+R)>>1;
    if (L==R)   {tree[rt].maxn=tree[rt].minn=tree[rt].lnum=tree[rt].rnum=delta;return;}
    if (x<=mid) modify(ln,x,delta);
    else    modify(rn,x,delta);
    push_up(rt);
}
seg query(int rt,int l,int r)
{
    int L=tree[rt].l,R=tree[rt].r,mid=(L+R)>>1;
    if (l<=L&&r>=R) return tree[rt];
    seg ret,ans1,ans2;ret=empty;
    bool _l=0,_r=0;
    if (l<=mid) _l=1,ans1=query(ln,l,r);
    if (r>mid)  _r=1,ans2=query(rn,l,r);
    if (_l)
    {
        if (_r) ret.lnum=ans1.lnum,ret.rnum=ans2.rnum,ret.maxn=max(ans1.maxn,ans2.maxn),ret.minn=min(ans1.minn,ans2.minn),ret.Gcd=gcd(gcd(ans1.Gcd,ans2.Gcd),Abs(ans1.rnum-ans2.lnum));
        else    ret=ans1;
    }
    else    ret=ans2;
    return ret;
}
int main()
{
     in(n);in(m);int opt,l,r,k;
     for (int i=1;i<=n;i++) in(a[i]);
     build();
     while (m--)
     {
        in(opt);
        if (opt==1) in(l),in(r),l^=cnt,r^=cnt,modify(1,l,r);
        else
        {
            in(l);in(r);in(k);l^=cnt;r^=cnt;k^=cnt;if (l>r) swap(l,r);
            seg t=query(1,l,r);
            if ((l==r)||(t.Gcd==k&&t.maxn-t.minn==(r-l)*k)) puts("Yes"),cnt++;
            else    puts("No");
        }
     }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值