//被标题吸引进来,无论如何都要切出来
题目大意
给你一个长度为n的序列,有m个操作,写一个程序支持以下两个操作:
1. 修改一个值
2. 给出三个数l,r,k,询问:如果把区间[l,r]的数从小到大排序,能否形成公差为k的等差数列。
强制在线
数据范围
n,m≤300000 0≤k,a[i]≤ 109
分析
首先特判k=0和l=r的情况。
然后考虑不满足上面两个条件的情况。
直接维护区间等差数列显然很难,那么考虑一下:如果区间[l,r] (l < r)排序后能形成公差为k(k>0)的等差数列,要满足什么条件?
1. 很显然,假设min是区间最小值,max是区间最大值,那么
min+k(r−l)=max
2. 区间相邻两个数之差的绝对值的gcd=k,可以自行脑补一下
3. 区间没有重复的数
维护区间信息
上述的前两个条件可以直接用线段树维护。
现在看第3个条件。
首先,序列出现过的值最多只有600000种,所以可以对于每个值开一个set,对应的id用一个map存起来。
然后维护一个pre[i],表示当前a[i]这个值,在i前面最后一次出现的位置。那么满足第3个条件,当且仅当区间[l,r]的pre的最大值小于l。这个也是用线段树维护。
然后看修改操作:在set上找前一个数、后一个数,然后修改相应的值。
时间复杂度
O(nlogn)
,要注意优化常数。
/**************************************************************
Problem: 4373
User: worldwide
Language: C++
Result: Accepted
Time:6148 ms
Memory:53008 kb
****************************************************************/
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <map>
#include <set>
#define id it->second
using namespace std;
const int maxn=300005,maxt=1048578,maxm=600005;
typedef long long LL;
int n,m,cnt,Min[maxt],Max[maxt],a[maxn],Max_pre[maxt],pre[maxn],next[maxn],Gcd[maxt],tot,l,r;
int mininum,maxinum,G;
bool flag;
char c;
map <int,int> num;
set <int> tree[maxm];
int read()
{
for (c=getchar();c<'0' || c>'9';c=getchar());
int x=c-48;
for (c=getchar();c>='0' && c<='9';c=getchar()) x=x*10+c-48;
return x;
}
int gcd(int x,int y)
{
if (x==0) return y;
if (y==0) return x;
return gcd(y,x%y);
}
void init(int l,int r,int x)
{
if (l==r)
{
Min[x]=Max[x]=a[l]; Max_pre[x]=pre[l];
if (l<n) Gcd[x]=abs(a[l]-a[l+1]);else Gcd[x]=1;
return;
}
int mid=l+r>>1;
init(l,mid,x<<1); init(mid+1,r,x<<1|1);
Min[x]=min(Min[x<<1],Min[x<<1|1]);
Max[x]=max(Max[x<<1],Max[x<<1|1]);
Max_pre[x]=max(Max_pre[x<<1],Max_pre[x<<1|1]);
Gcd[x]=gcd(Gcd[x<<1],Gcd[x<<1|1]);
}
void change_pre(int l,int r,int g,int New,int x)
{
if (l==r)
{
Max_pre[x]=pre[l]=New;
return;
}
int mid=l+r>>1;
if (g<=mid) change_pre(l,mid,g,New,x<<1);else change_pre(mid+1,r,g,New,x<<1|1);
Max_pre[x]=max(Max_pre[x<<1],Max_pre[x<<1|1]);
}
void change_a(int l,int r,int g,int x)
{
if (l==r)
{
Min[x]=Max[x]=a[l];
return;
}
int mid=l+r>>1;
if (g<=mid) change_a(l,mid,g,x<<1);else change_a(mid+1,r,g,x<<1|1);
Min[x]=min(Min[x<<1],Min[x<<1|1]);
Max[x]=max(Max[x<<1],Max[x<<1|1]);
Max_pre[x]=max(Max_pre[x<<1],Max_pre[x<<1|1]);
}
void change_gcd(int l,int r,int g,int x)
{
if (l==r)
{
Gcd[x]=abs(a[l]-a[l+1]);
return;
}
int mid=l+r>>1;
if (g<=mid) change_gcd(l,mid,g,x<<1);else change_gcd(mid+1,r,g,x<<1|1);
Gcd[x]=gcd(Gcd[x<<1],Gcd[x<<1|1]);
}
void query(int l,int r,int a,int b,int x)
{
if (l==a && r==b)
{
if (Max_pre[x]>=l) flag=1;
if (Min[x]<mininum) mininum=Min[x];
if (Max[x]>maxinum) maxinum=Max[x];
return;
}
int mid=l+r>>1;
if (b<=mid) query(l,mid,a,b,x<<1);
else if (a>mid) query(mid+1,r,a,b,x<<1|1);
else
{
query(l,mid,a,mid,x<<1); query(mid+1,r,mid+1,b,x<<1|1);
}
}
void query_gcd(int l,int r,int a,int b,int x)
{
if (l==a && r==b)
{
G=gcd(G,Gcd[x]);
return;
}
int mid=l+r>>1;
if (b<=mid) query_gcd(l,mid,a,b,x<<1);else
if (a>mid) query_gcd(mid+1,r,a,b,x<<1|1);else
{
query_gcd(l,mid,a,mid,x<<1); query_gcd(mid+1,r,mid+1,b,x<<1|1);
}
}
int main()
{
n=read(); m=read();
for (int i=1;i<=n;i++)
{
a[i]=read();
map <int,int> ::iterator it=num.find(a[i]);
if (it==num.end())
{
num.insert(make_pair(a[i],++tot));
tree[tot].insert(i);
pre[i]=0;
}else
{
tree[id].insert(i);
set <int> ::iterator ii=tree[id].find(i); ii--;
pre[i]=*ii; next[*ii]=i;
}
}
init(1,n,1);
while (m--)
{
int op=read();
if (op==1)
{
int x=read()^cnt,y=read()^cnt;
if (a[x]==y) continue;
map <int,int> ::iterator it=num.find(a[x]);
set <int> ::iterator ii=tree[id].find(x);
if (pre[x]>0)
{
next[pre[x]]=next[x];
if (next[x]>0) change_pre(1,n,next[x],pre[x],1);
}else if (next[x]>0) change_pre(1,n,next[x],0,1);
tree[id].erase(ii);
it=num.find(y);
if (it==num.end())
{
num.insert(make_pair(y,++tot));
tree[tot].insert(x);
pre[x]=next[x]=0;
}else
{
tree[id].insert(x);
ii=tree[id].find(x);
if (ii!=tree[id].begin())
{
ii--;
next[x]=next[*ii];
if (next[x]>0) change_pre(1,n,next[x],x,1);
next[*ii]=x;
pre[x]=*ii;
}
}
a[x]=y;
change_a(1,n,x,1);
if (x>1) change_gcd(1,n,x-1,1);
if (x<n) change_gcd(1,n,x,1);
}else
{
l=read()^cnt; r=read()^cnt; int k=read()^cnt;
if (l==r)
{
printf("Yes\n"); cnt++; continue;
}
mininum=(int)1e9; maxinum=flag=0;
query(1,n,l,r,1);
if (!k)
{
if (mininum==maxinum)
{
printf("Yes\n"); cnt++;
}else printf("No\n");
continue;
}
G=k;
query_gcd(1,n,l,r-1,1);
if (flag || mininum+(LL)k*(r-l)!=maxinum || G<k)
{
printf("No\n");
}else
{
printf("Yes\n"); cnt++;
}
}
}
return 0;
}