题意:有$n$个马,第$i$个马有初始能量$s_i$,每单位时间增加$r_i$,上限是$m_i$,多组询问$(t,l,r)$询问在时间$t$时$[l,r]$的能量和,询问完后把这段区间清零,保证$t$递增
两个月前曾经看到过这个题,当时看了官方题解+看了某神犇的中文题解之后觉得是不可做题所以就鸽了,今天回来一看原来还有这么简单的做法
直接上线段树,每个线段树节点存①最后被访问的时间$last$,没访问过记为$-2$,之前在不同时间被深♂入访问过记为$-1$②当前区间内的所有马,按照从零开始需要多少时间到达上限从小到大排序③$m$和$r$的前缀和
建树直接建,排个序然后处理前缀和,标记记为$-2$
询问时,设访问时间为$T$,访问到节点$x$,代表区间$[l,r]$
①若$last_x=-1$或询问区间未完整包含$[l,r]$,直接递归左右两边处理
②若$last_x=-2$,暴力计算答案,更新$last_x=T$
③若$last_x\geq0$,在当前区间内二分找到最后一个在$T-last_x$时间内会到达上限的马,前半部分答案为$\sum m$,后半部分答案为$\left(T-last_x\right)\sum r$,最后更新$last_x=T$
pushup:如果两个儿子标记相同,复制上来,否则记当前节点的标记为$-1$
pushdown:如果当前节点标记$\geq0$,直接往下覆盖标记
核心思想就是:除了第一次,后面每一次都可以当成初始能量为$0$来做,询问完后它又自己变回$0$了
开始处理询问后不会再有点的标记变为$-2$,所以暴力计算初始时的答案是可行的
#include<stdio.h> #include<algorithm> using namespace std; #define ll long long #define inf 2147483647 struct data{ int s,m,r,t; data(int x=0){t=x;} void gao(){ scanf("%d%d%d",&s,&m,&r); t=(r?m/r+(m%r>0):inf); } ll calc(ll u){return min(s+r*u,(ll)m);} }a[18][100010]; int Tt[400010]; ll Tm[18][400010],Tr[18][400010]; bool operator<(data a,data b){return a.t<b.t;} void build(int l,int r,int x,int d){ Tt[x]=-2; if(l==r){ a[d][l].gao(); Tm[d][l]=Tm[d][l-1]+a[d][l].m; Tr[d][l]=Tr[d][l-1]+a[d][l].r; return; } int mid=(l+r)>>1,i; build(l,mid,x<<1,d+1); build(mid+1,r,x<<1|1,d+1); for(i=l;i<=r;i++)a[d][i]=a[d+1][i]; sort(a[d]+l,a[d]+r+1); for(i=l;i<=r;i++){ Tm[d][i]=Tm[d][i-1]+a[d][i].m; Tr[d][i]=Tr[d][i-1]+a[d][i].r; } } void pushdown(int x){ if(Tt[x]>=0)Tt[x<<1]=Tt[x<<1|1]=Tt[x]; } void pushup(int x){ Tt[x]=Tt[x<<1]; if(Tt[x]!=Tt[x<<1|1])Tt[x]=-1; } ll query(int T,int L,int R,int l,int r,int x,int d){ int i; ll res=0; if(L<=l&&r<=R&&Tt[x]!=-1){ if(Tt[x]==-2){ for(i=l;i<=r;i++)res+=a[d][i].calc(T); }else{ i=upper_bound(a[d]+l,a[d]+r+1,data(T-Tt[x]))-a[d]-1; res=(T-Tt[x])*(Tr[d][r]-Tr[d][i])+Tm[d][i]-Tm[d][l-1]; } Tt[x]=T; return res; } pushdown(x); i=(l+r)>>1; if(L<=i)res+=query(T,L,R,l,i,x<<1,d+1); if(i<R)res+=query(T,L,R,i+1,r,x<<1|1,d+1); pushup(x); return res; } int main(){ int n,m,t,l,r; scanf("%d",&n); build(1,n,1,0); scanf("%d",&m); while(m--){ scanf("%d%d%d",&t,&l,&r); printf("%I64d\n",query(t,l,r,1,n,1,0)); } }