- 定义(42)的幂是坏的数,其余的数是好的数,初始给定一个长度为(n)的好数序列。
- 要求支持三种操作:单点询问;区间赋值;区间加法,重复这一过程直至所有数都是好的。
- (n,qle10^5,a_i,xle10^9)
线段树+势能分析
暂且不考虑区间赋值操作。
因为(42)的幂的数量很少,我们只要暴力修改,复杂度是(O(nlognlogV))的。
具体地,对于每个位置我们记下大于等于它的第一个(42)的幂与它的差值,修改时如果差值大于修改值直接打标记,否则暴力单点修改。
若一次修改之后最小差值为(0),说明存在坏的数,继续修改即可。
(ODT)处理区间赋值
然而这道题当中有区间赋值,如果每次直接区间赋值,可能导致很多数获得高势能。
因此我们必须要把每个相同的值域段信息统一维护在左端点上,而每次要询问某个点或修改某个区间的时候需要保证操作的是若干完整段,又要实现值域段的拆分。
这种区间赋值问题很容易想到(ODT)。
而在线段树上,对于每个值域段非左端点的部分,我们给它们打上抹杀标记,并在操作时忽略它们的影响。即若修改到这部分直接退出,若询问到这部分与下一个(42)的幂的最小差值直接返回(INF)。
代码:(O(nlognlogV))
#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 100000
#define LL long long
using namespace std;
int n;LL a[N+5];I LL Nxt(Con LL& x) {LL t=1;W(t<x) t*=42;return t;}
namespace FastIO
{
#define FS 100000
#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
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...);}
Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('
');}
}using namespace FastIO;
class SegmentTree
{
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]=min(D[x<<1]?1e18:V[x<<1],D[x<<1|1]?1e18:V[x<<1|1]))
#define PD(x) (D[x]&&(D[x<<1]=D[x<<1|1]=1,D[x]=F[x]=0),F[x]&&(T(x<<1,F[x]),T(x<<1|1,F[x]),F[x]=0))
#define T(x,v) (!D[x]&&(V[x]-=v,F[x]+=v))
int D[N<<2];LL V[N<<2],F[N<<2];
public:
I void Bd(PT)//建树
{
if(l==r) return (void)(V[rt]=Nxt(a[l])-a[l]);RI mid=l+r>>1;Bd(LT),Bd(RT),PU(rt);
}
I void U(CI x,Con LL& v,PT)//单点赋值
{
if(l==r) return (void)(a[l]=v,V[rt]=Nxt(a[l])-a[l],D[rt]=0);
RI mid=l+r>>1;PD(rt),x<=mid?U(x,v,LT):U(x,v,RT),PU(rt);
}
I void E(CI L,CI R,PT)//区间抹杀
{
if(D[rt]) return;if(L<=l&&r<=R) return (void)(D[rt]=1);RI mid=l+r>>1;
PD(rt),L<=mid&&(E(L,R,LT),0),R>mid&&(E(L,R,RT),0),PU(rt);
}
I void M(CI L,CI R,CI v,PT)//区间减法
{
if(D[rt]) return;if(l==r) return (void)(a[l]=Nxt(a[l])-V[rt]+v,V[rt]=Nxt(a[l])-a[l]);//单点直接修改
if(L<=l&&r<=R&&V[rt]>v) return (void)T(rt,v);RI mid=l+r>>1;//最小差值大于v才能直接区间打标记
PD(rt),L<=mid&&(M(L,R,v,LT),0),R>mid&&(M(L,R,v,RT),0),PU(rt);
}
I LL Q(CI x,PT)//单点询问
{
if(l==r) return Nxt(a[l])-V[rt];RI mid=l+r>>1;return PD(rt),x<=mid?Q(x,LT):Q(x,RT);
}
I LL Mn(CI L,CI R,PT)//区间询问最小差值
{
if(D[rt]) return 1e18;if(L<=l&&r<=R) return V[rt];RI mid=l+r>>1;
return PD(rt),min(L<=mid?Mn(L,R,LT):1e18,R>mid?Mn(L,R,RT):1e18);
}
}S;
namespace ODT//ODT维护区间赋值
{
#define IT set<Il>::iterator
struct Il {int l,r;I Il(CI a=0,CI b=0):l(a),r(b){}I bool operator < (Con Il& o) Con {return l<o.l;}};set<Il> s;
I IT Split(CI x)//拆出一个以x为左端点的区间
{
IT it=s.lower_bound(x);if(it->l==x) return it;RI l=(--it)->l,r=it->r;LL v=S.Q(l);
return S.U(x,v),s.erase(it),s.insert(Il(l,x-1)),s.insert(Il(x,r)).first;
}
I void Assign(CI l,CI r,CI v)//区间赋值
{
IT tr=Split(r+1),tl=Split(l);S.U(l,v),l^r&&(S.E(l+1,r),0),s.erase(tl,tr),s.insert(Il(l,r));
}
}
int main()
{
RI Qt,i;for(read(n,Qt),i=1;i<=n;++i) read(a[i]),ODT::s.insert(ODT::Il(i,i));S.Bd();
RI op,x,y,z;W(Qt--) switch(read(op,x),op)
{
case 1:ODT::Split(x),writeln(S.Q(x));break;case 2:read(y,z),ODT::Assign(x,y,z);break;
case 3:read(y,z),ODT::Split(y+1),ODT::Split(x),S.M(x,y,z);W(!S.Mn(x,y)) S.M(x,y,z);break;//最小差值为0说明存在坏的数
}return clear(),0;
}