传送门:bzoj4843
题解
设
a
i
a_i
ai为第
i
i
i个人借书的时间,
b
i
b_i
bi为第
i
i
i个人还书的时间,单次询问:
a
n
s
=
∑
max
(
b
i
−
a
i
,
0
)
ans=\sum \max(b_i-a_i,0)
ans=∑max(bi−ai,0)
每次在 b b b最前端插入 k k k个 0 0 0,相当于将 b b b整体右移 k k k后与 a a a对齐。
类似于bzoj4908开车,可以离散化之后算每一单位时间对于答案的贡献。
设 S 1 i , S 2 i S1_i,S2_i S1i,S2i分别表示前 i i i单位时间内借书,还书的人数, [ i − 1 , i ] [i-1,i] [i−1,i]这段时间的贡献次数即 c i = m a x ( S 1 i − 1 − S 2 i − 1 , 0 ) c_i=max(S1_{i-1}-S2_{i-1},0) ci=max(S1i−1−S2i−1,0)
最前端插入
k
k
k相当于将所有
c
i
−
k
c_i-k
ci−k。
处理出
c
i
c_i
ci排序后每次二分查找非零后缀和即可。
代码
#include<bits/stdc++.h>
#define mid (l+r>>1)
typedef long long ll;
using namespace std;
const int N=100010;
int n,m,ca,cb,v[N],sa,sb;
ll ans,ds[N],ss[N];
struct P{int v,id;bool operator <(const P&ky)const{return v<ky.v;}};
P p[N],a[N],b[N];
char cp;
inline int init(){
for(;;){
cp=getchar();
if(cp=='+') return 1;if(cp=='-') return 0;
}
}
int main(){
int i,x,y,z;
scanf("%d%d",&n,&m);
for(i=1;i<=n;++i){
x=init();scanf("%d%d",&y,&z);v[i]=y;
if(x) b[++cb]=(P){y,z},sb+=z;else a[++ca]=(P){y,z},sa+=z;
}
n=unique(v+1,v+n+1)-v-1;
for(i=1,x=1,y=1,z=0;i<=n;++i){
p[i].v=max(z,0);p[i].id=v[i]-v[i-1];
for(;x<=ca && a[x].v==v[i];++x) z+=a[x].id;
for(;y<=cb && b[y].v==v[i];++y) z-=b[y].id;
}
sort(p+1,p+n+1);ss[n+1]=ds[n+1]=0LL;
for(i=n;i;--i) ss[i]=ss[i+1]+(ll)p[i].v*p[i].id,ds[i]=ds[i+1]+p[i].id;
for(i=1;i<=n;++i) v[i]=p[i].v;
for(i=1;i<=m;++i){
scanf("%d",&x);
if(sb+x<sa) puts("INFINITY");
else{
y=upper_bound(v+1,v+n+1,x)-v;
printf("%lld\n",ss[y]-(ll)x*ds[y]);
}
}
return 0;
}