【BZOJ】4843: [Neerc2016]Expect to Wait-贡献拆分

传送门: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(biai,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] [i1,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(S1i1S2i1,0)

最前端插入 k k k相当于将所有 c i − k c_i-k cik
处理出 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值