【UOJ660】【IOI2021】candies(线段树)
undefined
- 有 个盒子,第 个盒子中至多装 颗糖果。
- 有 次操作,每次往第 个盒子中装入/取出 颗糖果,将操作后的糖果数向 取 ,向 取 。
- 求最终每个盒子中的糖果数。
重要结论
只考虑某一个糖果盒,不管向 取 和向 取 的限制,记录它在每次操作之后的糖果数 (即 的前缀和)。
发现如果存在一个区间 ,满足 是区间最大值,且 减 中的最小值大于 ,则在第 个操作后必然有 颗糖果。
同理,如果存在一个区间 ,满足 是区间最小值,且 中的最大值减 大于 ,则在第 个操作后必然没有糖果。
扫描线+线段树上二分
考虑用扫描线离线操作,这样就可以依次枚举每一个糖果盒,用线段树维护好针对这个糖果盒的修改。
然后线段树上二分,找出最靠后的一段后缀,满足其中最大值减最小值大于 。
对于较后面的那个最值,在它和最终值之间不存在相差大于 的值(否则就会选中以它开始的后缀),也就是说在这一过程中不用考虑向 取 和向 取 的问题,直接用 之差计算即可。
特殊地,如果找不到这样一段后缀,即对所有区间最大值减最小值都小于等于 ,则最小值所在位置必然没有糖果,从它开始计算答案即可。
代码:
#include "candies.h"
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Rg register
#define RI Rg int
#define Cn const
#define CI Cn int&
#define I inline
#define W while
#define N 200000
#define LL long long
#define INF (LL)1e18
using namespace std;
int n,Qt;struct OP {int x,t,v;I bool operator < (Cn OP& o) Cn {return x<o.x;}}s[2*N+5];
class SegmentTree
{
private:
#define PT CI l=0,CI r=Qt,CI rt=1
#define LT l,mid,rt<<1
#define RT mid+1,r,rt<<1|1
#define PU(x) (Mx[x]=max(Mx[x<<1],Mx[x<<1|1]),Mn[x]=min(Mn[x<<1],Mn[x<<1|1]))
#define PD(x) (F[x]&&(T(x<<1,F[x]),T(x<<1|1,F[x]),F[x]=0))
#define T(x,v) (Mx[x]+=v,Mn[x]+=v,F[x]+=v)
bool fg;LL mx,mn,Mx[N<<2],Mn[N<<2],F[N<<2];
I void G(CI c,PT)//线段树上二分
{
if(l==r) return (void)(fg=Mx[rt]>mx);RI mid=l+r>>1;PD(rt);//fg记录较靠前的是哪个最值
LL mxx=max(mx,Mx[rt<<1|1]),mnn=min(mn,Mn[rt<<1|1]);return mxx-mnn>=c?G(c,RT):(mx=mxx,mn=mnn,G(c,LT));//向右极差大于等于c就向右
}
I LL QV(CI x,PT) {if(l==r) return Mx[rt];RI mid=l+r>>1;PD(rt);return x<=mid?QV(x,LT):QV(x,RT);}//询问某个位置的值
public:
I void U(CI L,CI v,PT) {if(L<=l) return (void)T(rt,v);RI mid=l+r>>1;PD(rt),L<=mid&&(U(L,v,LT),0),U(L,v,RT),PU(rt);}//后缀修改
I int Q(CI c) {if(Mx[1]-Mn[1]<=c) return QV(Qt)-Mn[1];mx=-INF,mn=INF,G(c);return fg?QV(Qt)-mn:QV(Qt)-mx+c;}//找到最靠后的极差大于c的区间计算答案
}S;
vector<int> ans;vector<int> distribute_candies(vector<int> c,vector<int> l,vector<int> r,vector<int> v)
{
RI i,ct=0;for(n=c.size(),Qt=l.size(),i=0;i^Qt;++i) s[++ct]=(OP){l[i],i+1,v[i]},s[++ct]=(OP){r[i]+1,i+1,-v[i]};//离线
RI j=1;for(sort(s+1,s+ct+1),i=0;i^n;ans.push_back(S.Q(c[i++]))) W(j<=ct&&s[j].x==i) S.U(s[j].t,s[j].v),++j;return ans;//扫描线
}
待到再迷茫时回头望,所有脚印会发出光芒
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
2020-01-27 【BZOJ4008】[HNOI2015] 亚瑟王(DP)
2020-01-27 【BZOJ4416】 [SHOI2013] 阶乘字符串(状压DP)
2020-01-27 【BZOJ4524】[CQOI2016] 伪光滑数(堆的套路题)