CF679E 题解
题意简述
我们称
给出长度为
-
给定
,求 的值。 -
给定
,将区间 全赋值为给定的 。 -
给定
和 。先将 全部加上 ,然后不断如此操作下去一直到 都是“好数”为止。
题目分析
显然操作三并不能不停操作下去(操作有限次之后一定能找到两个相邻的
考虑使用线段树维护 lower_bound
查找新的“坏数”计算就行(直接枚举也行);操作三则先实现区间加,然后不断区间加直到
至于时间复杂度,根据刚刚的分析,每个位置只会被修改
代码实现
#include<bits/stdc++.h> using namespace std; int n,q,op,l,r; long long pow42[12],a[100010],x; struct node { int l,r;//左右端点。 long long mn,tag1,tag2;//mn:离下一个“坏数”距离的最小值;tag1 是区间加标记,tag2 是区间赋值标记。 }tr[400010]; template<typename T>void rd(T &x) { x=0; char c=getchar(); for(;c>'9'||c<'0';c=getchar()); for(;c<='9'&&c>='0';c=getchar()) x=(x<<3)+(x<<1)+c-'0'; }//快读 template<typename T>void wt(T x) { if(x>=10) wt(x/10); putchar(x%10+'0'); }//快写 long long calc(long long x) { return (*lower_bound(pow42+1,pow42+11,x))-x;//距下一个“坏数”的距离 } void pushup(int p) { tr[p].mn=min(tr[p<<1].mn,tr[p<<1|1].mn); }//子结点更新父结点 void addtag1(int p,long long tag) { if(tr[p].tag2)//区间赋值过就加到标记上 { tr[p].tag2+=tag; tr[p].mn=calc(tr[p].tag2); } else//没区间赋值过就直接打标记 { tr[p].mn-=tag; tr[p].tag1+=tag; } }//区间加打标记 void addtag2(int p,long long tag) { tr[p].tag2=tag; tr[p].mn=calc(tag); tr[p].tag1=0;//区间赋值之后,之前的区间加都失效 }//区间赋值打标记 void pushdown(int p) { if(tr[p].tag1) { addtag1(p<<1,tr[p].tag1); addtag1(p<<1|1,tr[p].tag1);//向左右子结点下传标记 tr[p].tag1=0;//清空标记 } if(tr[p].tag2) { addtag2(p<<1,tr[p].tag2); addtag2(p<<1|1,tr[p].tag2);//向左右子结点下传标记 tr[p].tag2=0;//清空标记 } } void build(int p,int l,int r)//建树 { tr[p].l=l,tr[p].r=r; if(l==r) { addtag2(p,a[l]);//为了方便询问,初始化就添上一个赋值标记 return; } int mid=l+r>>1; build(p<<1,l,mid); build(p<<1|1,mid+1,r); pushup(p); } void change1(int p,int l,int r,int x)//区间加 { if(tr[p].l>=l&&tr[p].r<=r/*区间全包含*/&&(tr[p].tag2||tr[p].mn>=x)/*若被区间赋值过或者区间加操作完后,没有比原来的“坏数”大,那么就可以直接打标记*/) { addtag1(p,x); return; } //否则就递归下去算 pushdown(p); int mid=tr[p].l+tr[p].r>>1; if(mid>=l) change1(p<<1,l,r,x); if(mid<r) change1(p<<1|1,l,r,x); pushup(p); } void change2(int p,int l,int r,int x)//区间赋值 { if(tr[p].l>=l&&tr[p].r<=r) { addtag2(p,x); return; } pushdown(p); int mid=tr[p].l+tr[p].r>>1; if(mid>=l) change2(p<<1,l,r,x); if(mid<r) change2(p<<1|1,l,r,x); pushup(p); } long long query(int p,int x)//单点查询,也可以直接二分实现 { if(tr[p].l==tr[p].r) return tr[p].tag2; int mid=tr[p].l+tr[p].r>>1; pushdown(p); if(mid>=x) return query(p<<1,x); else return query(p<<1|1,x); } int main() { pow42[0]=1ll; for(int i=1;i<=10;i++) pow42[i]=42ll*pow42[i-1];//预处理“坏数” rd(n),rd(q); for(int i=1;i<=n;i++) rd(a[i]); build(1,1,n); while(q--) { rd(op),rd(l); switch(op) { case 1: wt(query(1,l)); putchar('\n'); break; case 2: rd(r),rd(x); change2(1,l,r,x); break; case 3: rd(r),rd(x); do { change1(1,l,r,x); }while(tr[1].mn==0);//由于至少要操作一次,所以用 do-while。 } } return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!