2018.10.03 bzoj2527: [Poi2011]Meteors(整体二分)

传送门
整体二分经典题目。


直接整体二分当前区间中的询问操作需要下几场雨。
令当前询问区间是 [ l , r ] [l,r] [l,r],修改区间是 [ q l , q r ] , m i d = ( q l + q r ) / 2 [ql,qr],mid=(ql+qr)/2 [ql,qr],mid=(ql+qr)/2.
那么我们先计算 [ q l , m i d ] [ql,mid] [ql,mid]的贡献,将 [ l , r ] [l,r] [l,r]中达到目标的放在左区间,未达到目标的减去贡献之后放在右区间。
然后做这道题的时候有一个小技巧,我们将最开始的修改区间定成 [ 1 , k + 1 ] [1,k+1] [1,k+1],这样k场雨不够的会自动分到k+1。
代码:

#include<bits/stdc++.h>
#define N 300005
#define ll long long
using namespace std;
inline ll read(){
	ll ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
int n,m,k,ans[N];
ll bit[N];
struct Upd{int l,r;ll v;}upd[N];
struct Query{int id;ll k;vector<int>pos;}q[N],qtmp[N];
inline int lowbit(int x){return x&-x;}
inline void update(int x,ll v){for(int i=x;i<=m;i+=lowbit(i))bit[i]+=v;}
inline ll query(int x){ll ret=0;for(int i=x;i;i-=lowbit(i))ret+=bit[i];return ret;}
inline void solve(int l,int r,int ql,int qr){
	if(l>r)return;
	if(ql==qr){
		for(int i=l;i<=r;++i)ans[q[i].id]=ql;
		return;
	}
	int mid=ql+qr>>1,hd=l-1,tl=r+1;
	for(int i=ql;i<=mid;++i){
		if(upd[i].l<=upd[i].r)update(upd[i].l,upd[i].v),update(upd[i].r+1,-upd[i].v);
		else update(upd[i].l,upd[i].v),update(1,upd[i].v),update(upd[i].r+1,-upd[i].v);
	}
	for(int i=l;i<=r;++i){
		ll sum=0;
		for(int j=0;j<q[i].pos.size();++j){
			sum+=query(q[i].pos[j]);
			if(sum>=q[i].k)break;
		}
		if(sum>=q[i].k)qtmp[++hd]=q[i];
		else q[i].k-=sum,qtmp[--tl]=q[i];
	}
	for(int i=ql;i<=mid;++i){
		if(upd[i].l<=upd[i].r)update(upd[i].l,-upd[i].v),update(upd[i].r+1,upd[i].v);
		else update(upd[i].l,-upd[i].v),update(1,-upd[i].v),update(upd[i].r+1,upd[i].v);
	}
	for(int i=l;i<=r;++i)q[i]=qtmp[i];
	solve(l,hd,ql,mid),solve(tl,r,mid+1,qr);
}
int main(){
	n=read(),m=read();
	for(int i=1;i<=m;++i)q[read()].pos.push_back(i);
	for(int i=1;i<=n;++i)q[i].id=i,q[i].k=read();
	k=read();
	for(int i=1;i<=k;++i){
		int l=read(),r=read();
		ll v=read();
		upd[i]=(Upd){l,r,v};
	}
	solve(1,n,1,k+1);
	for(int i=1;i<=n;++i){
		if(ans[i]<=k)printf("%d\n",ans[i]);
		else puts("NIE");
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值