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

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


满足等差数列的条件:
1. g c d ( a i − a i + 1 ) % k = 0 ( l ≤ i &lt; r ) 1.gcd(a_i-a_{i+1})\%k=0 (l\le i&lt;r) 1.gcd(aiai+1)%k=0(li<r)
2. m a x ( a i ) − m i n ( a i ) = ( r − l ) ∗ k ( l ≤ i ≤ r ) 2.max(a_i)-min(a_i)=(r-l)*k (l\le i\le r) 2.max(ai)min(ai)=(rl)k(lir)
3. 区 间 内 没 有 重 复 的 数 3.区间内没有重复的数 3.


对于第一个,我们可以用线段树维护相邻两点值的差的gcd
对于第二个,直接维护最大最小值
对于第三个,我们记录区间内所有数上一次出现的位置,取max,如果这个位置大于l就重复了

然后对于上一次出现的数,可以直接暴力开set,大概只需要 n ∗ 2 n*2 n2个set就够用了

然后别挥霍内存就可以了


还有一个需要注意
就是k=0的情况要特判掉


#include<bits/stdc++.h>
using namespace std;
#define N 300001
#define LD (t<<1)
#define RD (t<<1|1)
int t1;
map<int,int> mp;
set<int> s[N<<1];
int n,m,val[N],pre[N];
struct Node{int gcd,minv,maxv,pre;}T[N<<2],tmp1,tmp2,res;
int t2;
int read(){
  int ans=0,w=1;char c=getchar();
  while(!isdigit(c)&&c!='-')c=getchar();
  if(c=='-')w=-1,c=getchar();
  while(isdigit(c))ans=(ans<<1)+(ans<<3)+c-'0',c=getchar();
  return ans*w;
}
int gcd(int a,int b){return b?gcd(b,a%b):a;}
Node merge(Node l,Node r){
  res.gcd=gcd(l.gcd,r.gcd);
  res.maxv=max(l.maxv,r.maxv);
  res.minv=min(l.minv,r.minv);
  res.pre=max(l.pre,r.pre);
  return res;
}
void build(int t,int l,int r){
  if(l>r)return;
  if(l==r){
    T[t].gcd=abs(val[l]-val[l+1]);
    T[t].minv=T[t].maxv=val[l];
    T[t].pre=pre[l];
    return;
  }
  int mid=(l+r)>>1;
  build(LD,l,mid);
  build(RD,mid+1,r);
  T[t]=merge(T[LD],T[RD]);
}
void modify(int t,int l,int r,int pos){
  if(l==r){
    T[t].gcd=abs(val[l]-val[l+1]);
    T[t].minv=T[t].maxv=val[l];
    T[t].pre=pre[l];
    return;
  }
  int mid=(l+r)>>1;
  if(pos<=mid)modify(LD,l,mid,pos);
  else modify(RD,mid+1,r,pos);
  T[t]=merge(T[LD],T[RD]);
}
Node query(int t,int l,int r,int L,int R){
  if(L<=l&&r<=R)return T[t];
  int mid=(l+r)>>1;
  if(R<=mid)return query(LD,l,mid,L,R);
  if(L>mid)return query(RD,mid+1,r,L,R);
  return merge(query(LD,l,mid,L,mid),query(RD,mid+1,r,mid+1,R));
}
int main(){
  freopen("4373.in","r",stdin);
  freopen("4373.out","w",stdout);
  scanf("%d%d",&n,&m);
  int cnt=0;
  for(int i=1;i<=n;i++){
    val[i]=read();
    if(!mp[val[i]]){
      mp[val[i]]=++cnt;
      s[cnt].insert(0);
      s[cnt].insert(n+1);
      s[cnt].insert(i);
      pre[i]=0;
    }else{
      s[mp[val[i]]].insert(i);
      pre[i]=*--(s[mp[val[i]]].lower_bound(i)); 
    }
  }
  val[n+1]=val[n];
  build(1,1,n);
  int las=0;
  while(m--){
    int op=read();
    if(op==1){
      int x=read()^las,y=read()^las;
      int t=mp[val[x]];
      int tmp=*s[t].upper_bound(x);
      if(tmp!=n+1){
        pre[tmp]=pre[x];
        modify(1,1,n,tmp);
      }
      s[t].erase(x);
      if(!mp[y]){
        mp[y]=++cnt;
        s[cnt].insert(0);
        s[cnt].insert(n+1);
        s[cnt].insert(x);
        pre[x]=0;
      }else{
        s[mp[y]].insert(x);
        pre[x]=*--s[t].lower_bound(x);
      }
      t=mp[y];
      tmp=*s[t].upper_bound(x);
      if(tmp!=n+1){
        pre[tmp]=x;
        modify(1,1,n,x);
      }
      if(x==n)val[n+1]=y;
      val[x]=y;
      modify(1,1,n,x);
      if(x>1)modify(1,1,n,x-1);
    }else{
      int l=read()^las,r=read()^las,k=read()^las;
      if(l==r){printf("Yes\n");++las;continue;}
      tmp1=query(1,1,n,l,r-1);
      tmp2=merge(tmp1,query(1,1,n,r,r));
      if(!k){
        if(tmp2.minv==tmp2.maxv)++las,printf("Yes\n");
        else printf("No\n");
        continue;
      }
      if(tmp1.gcd%k){printf("No\n");;continue;}
      if(tmp2.pre>=l){printf("No\n");continue;}
      if(tmp2.maxv-tmp2.minv!=(r-l)*k){printf("No\n");continue;}
      las++;
      printf("Yes\n");
    }
  }
  printf("\n");
  return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值