把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

【洛谷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;
}
posted @ 2021-05-25 16:22  TheLostWeak  阅读(110)  评论(0编辑  收藏  举报