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

题解 P4041 [AHOI2014/JSOI2014] 奇怪的计算器

洛谷

题意

Q 个询问,n 个操作,一共有 4 种:

  1. +a:结果加上 a
  2. a:结果减去 a
  3. ×a;结果乘上 a
  4. @a:结果加上 a×XX 是一开始输入的数)。 将每个询问放在 n 个操作中过一遍,同时每次处理完后,倘若小于 L,赋值为 L;倘若大于 R,赋值为 R

暴力

按照题意模拟即可。

分析

我们可以发现,我们所有数的经历的过程是一样的,我们可以用线段树来维护我们的 6 中操作:

  1. 区间加;
  2. 区间减(同 1);
  3. 区间乘;
  4. 区间加的变种;
  5. 区间取至最大值;
  6. 区间取至最小值。

这么算来,我们似乎是要维护 5 种 tag,10 种值。
想想都让人头皮发麻,怎么都应该是黑题才对。

但是实际上,我们的其中操作其实并不会改变期间相邻数的大小关系,因此,我们的 5、6 操作就变成了区间覆盖与一个二分(找到最后一个小于 L 或 第一个大于 R)啦。

总结一下,我们需要维护区间乘、区间加、区间加变形、区间覆盖、单点查询。

看起来还是非常复杂,但是我们用 cp×tag1p+tag2p 的形式,就可以将区间乘,区间加,区间覆盖融为一体。
最后的区间加变种,地位与区间加等同,与区间加的维护大致相同。

struct SEG {
#define ls p<<1
#define rs p<<1|1
int c[N<<2],tag[N<<2],tag2[N<<2],tag3[N<<2];
inline void cl(int p,int x,int y,int L,int R) {
tag3[p]*=x;
tag2[p]=tag2[p]*x+y;
tag[p]=tag[p]*x;
}
inline void cl(int p,int x) {
tag3[p]+=x;
}
inline void pushdown(int p,int L,int R) {
int mid=L+R>>1;
cl(ls,tag[p],tag2[p],L,mid),cl(rs,tag[p],tag2[p],mid+1,R);
cl(ls,tag3[p]),cl(rs,tag3[p]);
tag[p]=1,tag2[p]=tag3[p]=0;
}
inline void build(int p,int L,int R) {
tag[p]=1,tag2[p]=tag3[p]=0;
if(L==R) {
c[p]=qu[L].x;
return ;
}
int mid=L+R>>1;
build(ls,L,mid),build(rs,mid+1,R);
}
inline void change(int p,int L,int R,int l,int r,int x,int y) {
if(l>r) return ;
if(l<=L&&R<=r) {
cl(p,x,y,L,R);
return ;
}
int mid=L+R>>1;
pushdown(p,L,R);
if(l<=mid) change(ls,L,mid,l,r,x,y);
if(mid<r) change(rs,mid+1,R,l,r,x,y);
}
inline void change(int p,int L,int R,int l,int r,int x) {
if(l>r) return ;
if(l<=L&&R<=r) {
cl(p,x);
return ;
}
int mid=L+R>>1;
pushdown(p,L,R);
if(l<=mid) change(ls,L,mid,l,r,x);
if(mid<r) change(rs,mid+1,R,l,r,x);
}
inline int query(int p,int L,int R,int x) {
if(L==R) return c[p]*tag[p]+tag2[p]+tag3[p]*c[p];
pushdown(p,L,R);
int mid=L+R>>1;
if(x<=mid) return query(ls,L,mid,x);
if(mid<x) return query(rs,mid+1,R,x);
}
#undef ls
#undef rs
} tree;
inline void solvel() {
int l=1,r=m,res=0;
while(l<=r) {
int mid=l+r>>1;
int x=tree.query(1,1,m,mid);
if(x<L) {
res=mid;
l=mid+1;
} else r=mid-1;
}
tree.change(1,1,m,1,res,0,L);
}
inline void solver() {
int l=1,r=m,res=m+1;
while(l<=r) {
int mid=l+r>>1;
int x=tree.query(1,1,m,mid);
if(x>R) {
res=mid;
r=mid-1;
} else l=mid+1;
}
tree.change(1,1,m,res,m,0,R);
}

总结一下,这题其实利用了线段树的区间修改的时间复杂度,操作的不改变相对关系。

posted @   djh0314  阅读(13)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
浏览器标题切换
浏览器标题切换end
点击右上角即可分享
微信分享提示