题解:
题目中要求查询一段区间内的数能否构成公差为K的等差数列。由于等差数列本身不好维护,那么我们就可以去找区间内的数能构成等差数列的等价条件,再维护这些条件即可。
通过看别人的题解,我们可以得到这些条件:
1.这些数不相同。(废话)
2.最大值减去最小值等于(l-r)k。(直接套公式,还是挺显然的)
3.所有相邻两数的差的gcd都为k,即都为k的倍数。(????????)
等差数列肯定满足以上条件,所以我们只需要来伪证一下它的充分性即可。
首先,所有相邻两数的差都是k的倍数,那么我们可以知道任意两数的差都是k的倍数,因为 任意两数的差 都可以用几个 相邻两数的差 通过加减得到。
所以,所有数与最小值的差都是k的倍数。这段区间最大值与最小值的差是(l-r)k,所以每个数与最小值做差得到的值不会超过(l-r)k那么这些差就只有0,k,2k,3k…(l-r)k这(l-r+1)种情况。又因为这段区间有(l-r+1)个且互不相同的数,所以只有可能是把拿(l-r+1)种情况都占满了的,也就是等差数列。
在伪证完了之后,我们来考虑如何维护这些性质。鉴于是区间查询,所以考虑线段树维护。最大值和最小值不用说,gcd只需要记录一个数和它下一个数的差,再与其它差求gcd,查询只需要查询(l,r-1)即可。至于如何确定这些数不同,我们需要用一个pre数组记录每个位置的 上一个与这个位置有相同值的位置的编号,查询时只需要查询最大的pre,只要它小于l就行。我们可以先将值离散,再把拥有相同值的位置的编号丢将进set里让它自动排序,就可以维护pre数组了。
代码如下(自带常数大性质的我开了o2才水过,还不如去维护区间平方和和立方和碰碰运气)
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 3e5+5;
int readint()
{
int x=0,f=1;char s=getchar();
#define sc (s=getchar())
while(s<'0'||s>'9'){if(s=='-')f=-1;sc;}
while(s>='0'&&s<='9'){x=(x<<3)+(x<<1)+(s^48);sc;}
return x*f;
}
int gcd(int a,int b)
{
return b?gcd(b, a%b):a;
}
struct zz
{
int l,r;
int s;
int goal_l,goal_r;
int maxx,minn;
}tr[maxn<<2];
int a[maxn];
int fabs(int x)
{
if(x<0)
x=-x;
return x;
}
void push_up(int num)
{
tr[num].goal_l=tr[num<<1].goal_l;
tr[num].goal_r=tr[num<<1|1].goal_r;
int xx=fabs(tr[num<<1].goal_r-tr[num<<1|1].goal_l);
tr[num].s=gcd(gcd(tr[num<<1].s,tr[num<<1|1].s),xx);
tr[num].maxx=max(tr[num<<1].maxx,tr[num<<1|1].maxx);
tr[num].minn=min(tr[num<<1].minn,tr[num<<1|1].minn);
}
void build(int num,int l,int r)
{
tr[num].l=l;
tr[num].r=r;
if(l!=r)
{
int mid=(l+r)/2;
build(num<<1,l,mid);
build(num<<1|1,mid+1,r);
push_up(num);
}
else
{
tr[num].goal_l=a[l];
tr[num].goal_r=a[l];
tr[num].maxx=a[l];
tr[num].minn=a[l];
}
}
void updata(int num,int l,int r,int x)
{
if(l<=tr[num].l&&tr[num].r<=r)
{
tr[num].goal_l=x;
tr[num].goal_r=x;
tr[num].maxx=x;
tr[num].minn=x;
}
else
{
int mid=(tr[num].l+tr[num].r)>>1;
if(l<=mid)
updata(num<<1,l,r,x);
if(r>mid)
updata(num<<1|1,l,r,x);
push_up(num);
}
}
zz nx;
zz ask(int num,int l,int r)
{
if(l<=tr[num].l&&tr[num].r<=r)
return tr[num];
else
{
int mid=(tr[num].l+tr[num].r)>>1;
zz ans=nx;
zz ans1,ans2;
bool f1=0,f2=0;
if(l<=mid)
{
f1=1;
ans1=ask(num*2,l,r);
}
if(r>mid)
{
f2=1;
ans2=ask(num*2+1,l,r);
}
if(f1)
if(f2)
{
int xx=fabs(ans1.goal_r-ans2.goal_l);
ans.s=gcd(gcd(ans1.s,ans2.s),xx);
ans.maxx=max(ans1.maxx,ans2.maxx);
ans.minn=min(ans1.minn,ans2.minn);
ans.goal_l=ans1.goal_l;
ans.goal_r=ans2.goal_r;
}
else
ans=ans1;
else
ans=ans2;
return ans;
}
}
int main()
{
//freopen("card.in","r",stdin);
//freopen("card.out","w",stdout);
int n,m;
n=readint();
m=readint();
for(int i=1;i<=n;i++)
a[i]=readint();
build(1,1,n);
int l,r,k;
int sx=0;
for(int i=1;i<=m;i++)
{
int c=readint();
if(c==1)
{
l=readint(),r=readint();
l=l^sx;
r=r^sx;
updata(1,l,l,r);
}
else
{
l=readint(),r=readint(),k=readint();
l=l^sx;
r=r^sx;
k=k^sx;
zz t=ask(1,l,r);
if(t.s==k&&(t.maxx-t.minn)==k*(r-l)||l==r)
{
printf("Yes\n");
sx++;
}
else
printf("No\n");
}
}
return 0;
}