codeforces 679e Bear and Bad Powers of 42

传送门: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\n",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;
}

  

转载于:https://www.cnblogs.com/chadinblog/p/6336437.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值