【洛谷5278】算术天才⑨与等差数列(线段树)
- 给定一个长度为\(n\)的序列。
- \(q\)次操作,分为两种:单点修改;询问区间\([l,r]\)内的数是否能重排为一个公差为\(d\)的等差数列。
- \(n,m\le3\times10^5\),强制在线
等差数列的充要条件
考虑能重排为一个公差为\(d\)的等差数列的充要条件:
- 极差为\((r-l)\times d\)。 这只需要维护区间最大值和区间最小值。
- 数不重复。 这只要记录每个数上一次出现的位置\(pre_i\),求出区间\(pre\)最大值判断是否小于\(l\)。要求\(pre_i\)只要对于每种值开一个\(set\)维护。
- 相邻两数之差的\(gcd\)是\(d\)的倍数。 另开一棵线段树存储相邻两数之差,并维护区间\(gcd\)即可。
注意\(d=0\)要特判,此时相当于是判断区间内的所有数是否都一样,这也就意味着极差为\(0\)。
代码:\(O(nlog^2n)\)
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 300000
using namespace std;
int n,m,a[N+5];I int gcd(CI x,CI y) {return y?gcd(y,x%y):x;}
namespace FastIO
{
#define FS 100000
#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
char oc,FI[FS],*FA=FI,*FB=FI;
Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
}using namespace FastIO;
struct Data
{
int Mn,Mx,P;I Data(CI a=0,CI b=0,CI c=0):Mn(a),Mx(b),P(c){}
I Data operator + (Con Data& o) Con {return Data(min(Mn,o.Mn),max(Mx,o.Mx),max(P,o.P));}
};
class SegmentTree1
{
private:
#define PT CI l=1,CI r=n,CI rt=1
#define LT l,mid,rt<<1
#define RT mid+1,r,rt<<1|1
#define PU(x) (V[x]=V[x<<1]+V[x<<1|1])
Data V[N<<2];
public:
I void U(CI x,CI v,CI p,PT)//单点修改值和pre
{
if(l==r) return (void)(V[rt]=Data(v,v,p));RI mid=l+r>>1;x<=mid?U(x,v,p,LT):U(x,v,p,RT),PU(rt);
}
I Data Q(CI L,CI R,PT)//询问区间最大最小值、pre最大值
{
if(L<=l&&r<=R) return V[rt];RI mid=l+r>>1;if(R<=mid) return Q(L,R,LT);
if(L>mid) return Q(L,R,RT);return Q(L,mid,LT)+Q(mid+1,R,RT);
}
}S1;
class SegmentTree2
{
private:
#define PT CI l=1,CI r=n-1,CI rt=1
#define LT l,mid,rt<<1
#define RT mid+1,r,rt<<1|1
#define PU(x) (D[x]=gcd(D[x<<1],D[x<<1|1]))
int D[N<<2];
public:
I void U(CI x,CI d,PT)//单点修改差值
{
if(l==r) return (void)(D[rt]=d);RI mid=l+r>>1;x<=mid?U(x,d,LT):U(x,d,RT),PU(rt);
}
I int Q(CI L,CI R,PT)//询问区间差值gcd
{
if(L<=l&&r<=R) return D[rt];RI mid=l+r>>1;if(R<=mid) return Q(L,R,LT);
if(L>mid) return Q(L,R,RT);return gcd(Q(L,mid,LT),Q(mid+1,R,RT));
}
}S2;
namespace Pre//pre相关
{
int cnt;map<int,int> id;I int ID(CI x) {return ~x?(id.count(x)?id[x]:id[x]=++cnt):0;}//离散化
set<int> S[2*N+5];I void Ins(CI x,CI v)//加入下标为x的元素v
{
RI o=ID(v);S1.U(x,v,S[o].empty()?0:*--S[o].end()),S[o].insert(x);//判断之前是否出现过求出前驱
}
I void U(CI x,CI a,CI b)//把下标为x的元素由a改为b
{
#define pre(S,x) (S.lower_bound(x)!=S.begin()?*--S.lower_bound(x):0)//前驱
#define nxt(S,x) (S.upper_bound(x)!=S.end()?*S.upper_bound(x):0)//后继
RI o=ID(a),p=pre(S[o],x),q=nxt(S[o],x);S[o].erase(x),q&&(S1.U(q,a,p),0);//从原set中删去,修改后继的前驱
o=ID(b),p=pre(S[o],x),q=nxt(S[o],x),q&&(S1.U(q,b,x),0),S[o].insert(x),S1.U(x,b,p);//加入新set,更新后继及当前点的前驱
}
}
I bool Check(CI l,CI r,CI d)//检验[l,r]是否能重排为一个公差为d的等差数列
{
if(l==r) return 1;Data t=S1.Q(l,r);RI g=S2.Q(l,r-1);return t.Mx-t.Mn==1LL*d*(r-l)&&(!d||t.P<l&&!(g%d));//极差为(r-l)*d;不重复;相邻差值gcd为d倍数(特判d=0)
}
int main()
{
RI Qt,i;for(read(n,Qt),i=1;i<=n;++i) read(a[i]),Pre::Ins(i,a[i]),i^1&&(S2.U(i-1,abs(a[i]-a[i-1])),0);//初始化
RI op,x,y,z,t=0;W(Qt--) read(op,x,y),x^=t,y^=t,
op==1?(Pre::U(x,a[x],y),a[x]=y,x^n&&(S2.U(x,abs(a[x+1]-a[x])),0),x^1&&(S2.U(x-1,abs(a[x]-a[x-1])),0))//更新值;更新差值
:(read(z),z^=t,puts(Check(x,y,z)?(++t,"Yes"):"No"));//询问
return 0;
}
待到再迷茫时回头望,所有脚印会发出光芒