传送门:http://codeforces.com/contest/679/problem/E
题目意思很清晰,给你一个序列,要求你完成以下三个操作:
1.输出A[i]
2.将[a,b]区间的所有数字变成c
3.将[a,b]区间的每个数加上c,若此时区间内有42的整次幂(1,42,422,423,424...),则一直进行此操作,直到区间内没有这类数;
正解:
线段树拓展题目;
先不考虑操作2,只考虑操作3;
我可以给每个数字设置一个等级,即处在(1,42)的数1级,(42,422)的数2级,以此类推,再记录每个数与它最近的大于它的42的整次幂的距离;
用线段树记录区间最大值,一旦最大值大于0,对线段树向下走,暴力升级,即哪里的最大值大于0,就去哪里升级,由于指数上升很快,每个数最多被升级log42(数值范围)次;
再考虑操作2,操作2会将一整个区间变成一个数,这时如果暴力升级可能会被卡掉,不如在线段树里记录一下,这段区间的数是否相同,如果相同,对整段区间进行升级即可,没必要再下去了;
考虑一下时间复杂度,操作3的复杂度由于指数的保证,均摊不会超过O(log22n),操作2复杂度相当,操作1复杂度O(logn),总复杂度O(nlog22n),过掉这道题还是比较轻松的;
对于这道题需要注意的是,lazy标记的下传,需要头脑十分清晰;
#include<iostream> #include<cstdio> #include<cstring> #include<string> #include<cmath> #include<cstdlib> #include<ctime> #include<algorithm> using namespace std; #define mid ((l+r)>>1) #define LL long long #define FILE "dealing" #define up(i,j,n) for(LL i=(j);i<=(n);i++) #define pii pair<LL,LL> LL read(){ LL x=0,f=1,ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+ch-'0',ch=getchar(); return f*x; } const LL maxn=1000100,inf=1000000000000000LL,mod=998244353; LL n,m,a[maxn]; LL x,y,c; LL f[maxn],d[maxn],g[maxn],del[maxn],Max[maxn],mi[20],flag[maxn],level; pii ch(LL x){up(i,0,11)if(x<mi[i])return make_pair(i,x-mi[i]);} void upp(LL o){ while(f[o]>0){ g[o]++; Max[o]=f[o]=Max[o]+mi[g[o]-1]-mi[g[o]]; } } void pushdown(LL x){ if(del[x]){ Max[x<<1]+=del[x],Max[x<<1|1]+=del[x]; if(d[x<<1])f[x<<1]+=del[x],upp(x<<1); else del[x<<1]+=del[x]; if(d[x<<1|1])f[x<<1|1]+=del[x],upp(x<<1|1); else del[x<<1|1]+=del[x]; del[x]=0; } if(d[x]){ Max[x<<1]=Max[x<<1|1]=f[x]; del[x<<1]=del[x<<1|1]=0; g[x<<1]=g[x<<1|1]=g[x]; d[x<<1]=d[x<<1|1]=1; f[x<<1]=f[x<<1|1]=f[x]; d[x]=0;f[x]=0; } } void updata(LL x){ Max[x]=max(Max[x<<1],Max[x<<1|1]); g[x]=(Max[x<<1]>Max[x<<1|1])?g[x<<1]:g[x<<1|1]; if(flag[x<<1]&&flag[x<<1|1]&&Max[x<<1]==Max[x<<1|1]&&g[x<<1]==g[x<<1|1])flag[x]=1; else flag[x]=0; } LL query(LL l,LL r,LL o){//求[x,y]区间最大值 if(l>y||r<x)return -inf; if(l>=x&&r<=y){level=g[o];return Max[o];} pushdown(o); return max(query(l,mid,o<<1),query(mid+1,r,o<<1|1)); } void change(LL l,LL r,LL o){//将[x,y]区间赋为c if(l>y||r<x)return; if(l>=x&&r<=y){ d[o]=1;flag[o]=1; del[o]=0; pii x=ch(c); f[o]=x.second; Max[o]=x.second; g[o]=x.first; return; } pushdown(o); change(l,mid,o<<1); change(mid+1,r,o<<1|1); updata(o); } void Change(LL l,LL r,LL o){//将[x,y]区间加c if(l>y||r<x)return; if(l>=x&&r<=y){ Max[o]+=c; if(d[o]){ f[o]+=c; if(f[o]>0){ while(Max[o]>0){ g[o]++; Max[o]=f[o]=Max[o]+mi[g[o]-1]-mi[g[o]]; } } } else del[o]+=c; return; } pushdown(o); Change(l,mid,o<<1); Change(mid+1,r,o<<1|1); updata(o); } void upgrade(LL l,LL r,LL o){//将[1,n]中的所有点升级 if(flag[o]){ d[o]=1; while(Max[o]>0){ g[o]++; f[o]=Max[o]=Max[o]+mi[g[o]-1]-mi[g[o]]; } return; } pushdown(o); if(Max[o<<1]>0)upgrade(l,mid,o<<1); if(Max[o<<1|1]>0)upgrade(mid+1,r,o<<1|1); updata(o); } void build(LL l,LL r,LL o){ if(l==r){ flag[o]=1; pii x=ch(a[l]); Max[o]=x.second; g[o]=x.first; flag[o]=1; return; } build(l,mid,o<<1); build(mid+1,r,o<<1|1); updata(o); } int main(){ n=read();m=read(); mi[0]=1; up(i,1,11)mi[i]=mi[i-1]*42; up(i,1,n)a[i]=read(); build(1,n,1); while(m--){ LL ch=read(); if(ch==1){ y=x=read(); printf("%lld ",query(1,n,1)+mi[level]); } if(ch==2){ x=read(),y=read(),c=read(); change(1,n,1); } if(ch==3){ x=read(),y=read(),c=read(); do{ Change(1,n,1); upgrade(1,n,1); if(Max[1]<0)break; }while(1); } } return 0; }