1 条题解
-
0
自动搬运
来自洛谷,原作者为

xzx34
但就是因为你能这样思考,才意味着你拥有着,能让你越来越强大的力量搬运于
2025-08-24 22:14:56,当前版本为作者最后更新于2020-04-10 22:44:26,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
题目要求你维护一段序列,支持操作包括:
1 区间修改为同一个数
2 令s[i] 表示当前前缀和,最小的i使得h<=s[i]并输出i-1;
大概思考一下,我们能想到用线段树解决这一问题。
首先是区间修改,线段树基本操作,不谈。
然后是怎么处理操作2,我们可以在每个线段树上的每个节点上存上
sum 表示这个节点下数值和
lsum 表示这个节点对应的区间从左开始的最大值
然后就可以处理了。。。
设计一个“爬”函数,根节点开始爬。
如果lson的lsum大于h,则爬向lson;否则爬向rson,h-=lson的sum。
这里的正确性真的是很显然的,建议手玩。
但是普通的线段树过不了这题。有n=1e9的瓶颈,空间会炸。
有两种方法解决这一限制,动态开点线段树和离散化。我使用的是前者,相对好打一点。
然后有很温柔的小哥哥让我教怎么打动态开点线段树。其实动态开点线段树真的很好打好想。我们进行的是区间修改,因为有懒标记的存在,几乎所有的操作都不会访问到叶节点。因此,我们很多提前开出来的节点几乎从来不会被访问!这就很亏,很烦。
考虑一个暴力的想法:随着我的访问去开点。对于每一个节点,如果我是第一次访问它,我就把它设为++tot,不然它的存在与否就不重要。
这就是动态开点线段树的思路,实际上通过这种操作,你的空间复杂度就不再与序列长度挂钩,而是与操作次数挂钩。非常的舒服。
代码如下:
#include <bits/stdc++.h> #define re register int #define il inline #define ll int #define ls t[k].lson #define rs t[k].rson using namespace std; const int inf=1e9; il int read(){ char c=getchar();int z=0,f=1; while(c!='-'&&(c>'9'||c<'0')) c=getchar(); if(c=='-') f=-1,c=getchar(); while(c>='0'&&c<='9') z=(z<<1)+(z<<3)+c-'0',c=getchar(); return z*f; } int tot=1; struct TREE{ int l,r,lson,rson; ll lsum,sum,add; bool book; }t[4500005]; il void check(int k){//动态开点 int mid=t[k].l+t[k].r>>1; if(!ls){ls=++tot;t[ls].l=t[k].l,t[ls].r=mid;} if(!rs){rs=++tot;t[rs].r=t[k].r,t[rs].l=mid+1;} } il void spread(int k){//基本操作下传信息 if(!t[k].book) return ;t[k].book=0; t[ls].add=t[rs].add=t[k].add;t[ls].book=t[rs].book=1; t[ls].sum=(t[ls].r-t[ls].l+1)*t[k].add; t[rs].sum=(t[rs].r-t[rs].l+1)*t[k].add; if(t[ls].sum>0) t[ls].lsum=t[ls].sum; else t[ls].lsum=t[k].add; if(t[rs].sum>0) t[rs].lsum=t[rs].sum; else t[rs].lsum=t[k].add; } il void change(int k,int l,int r,ll d){//基本操作修改 if(t[k].r<l||t[k].l>r) return; if(t[k].l>=l&&t[k].r<=r){ t[k].add=d;t[k].sum=d*(t[k].r-t[k].l+1);t[k].book=1; if(t[k].sum>0) t[k].lsum=t[k].sum; else t[k].lsum=t[k].add; return ; } check(k);//防止越界动态开点 spread(k); change(ls,l,r,d);change(rs,l,r,d); t[k].sum=t[ls].sum+t[rs].sum;//更新关键信息 t[k].lsum=max(t[ls].sum+t[rs].lsum,t[ls].lsum);//更新关键信息 } int ans; il void pa(int k,ll h){//爬 if(t[k].l==t[k].r){ if(h>=t[k].lsum) ans=t[k].l; else ans=t[k].l-1; return ; } check(k);spread(k); if(h>=t[ls].lsum) pa(rs,h-t[ls].sum); else pa(ls,h); } int n;char c; int main (){ n=read();t[1].l=1,t[1].r=n; while(1){ cin>>c;if(c=='E') return 0; if(c=='Q'){ ll h=read();pa(1,h); cout<<ans<<'\n'; } if(c=='I'){ int l=read(),r=read(),d=read(); change(1,l,r,d); } } return 0; }这篇代码可以拿下96分,不能AC,还是会被卡空间。
这篇代码的空间复杂度还能继续优化。
为了便于初学者理解,我存了每个节点的l,r。
这个空间开销是多余的,可以在函数中保留l,r信息,不需要存下来。
把这一点优化了以后就可以愉快的AC了。希望对读者有帮助吧。
- 1
信息
- ID
- 4854
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者