2018ACM/ICPC南京站网络赛G Lpl and Energy-saving Lamps(补题)

题目链接

题意:已知n个房间,每个房间需要有ai个灯,主角在给所有房间都装好节能灯之前,会每个月购买m个节能灯,同时每个月按房间编号从小到大依次给能完全满足需求的房间装节能灯,剩下的节能灯留到下个月用。接下来q个查询,询问第bi个月后已经装好灯的房间数和该月最后剩下的节能灯数。

由题可知,每次更换节能灯需要得到节能灯需求数目小于拥有数目的房间编号,那么我们可以通过线段树维护区间最小值,查询的时候优先查询更左边最小值小于等于拥有数目的区间,就可以保证查询到的是满足要求且房间编号最小的房间。当存在这样一个房间时,返回房间编号,然后更新拥有的节能灯数目,同时将该房间要求数量更新为INF即可保证不影响后面的操作;否则返回-1,代表目前已经没有比要求的比拥有的节能灯数目更小的房间了。

因为查询的月份最多1e5,因此可以先求出每个月的结果存到数组里,之后O(1)查询即可。

代码如下:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define For(i,a,b) for(int i=a;i<=b;i++)
#define INF 0x3f3f3f3f
const int N = 100005;
const int mod=1e9+7;
template<class T>
inline bool scan_d(T &ret){
	char c;
	int sgn;
	if(c=getchar(),c==EOF) return 0;
	while(c!='-'&&(c<'0'||c>'9')) c=getchar();
	sgn=(c=='-')?-1:1;
	ret=(c=='-')?0:(c-'0');
	while(c=getchar(),c>='0'&&c<='9') ret=ret*10+c-'0';
	ret*=sgn;
	return 1;
}
int sum[N<<2],a[N],ans1[N],ans2[N];
void build(int rt,int l,int r){
	if(l==r){
		sum[rt]=a[l];return;
	}
	int mid=(l+r)>>1;
	build(rt<<1,l,mid);
	build(rt<<1|1,mid+1,r);
	sum[rt]=min(sum[rt<<1],sum[rt<<1|1]);
}
void update(int rt,int l,int r,int x){
	if(l==r){
		sum[rt]=INF;return;
	}
	int mid=(l+r)>>1;
	if(x<=mid) update(rt<<1,l,mid,x);
	else update(rt<<1|1,mid+1,r,x);
	sum[rt]=min(sum[rt<<1],sum[rt<<1|1]);
}
int query(int rt,int l,int r,int x){
	if(l==r){
		return l;
	}
	int mid=(l+r)>>1;
	if(sum[rt<<1]<=x) return query(rt<<1,l,mid,x);
	if(sum[rt<<1|1]<=x) return query(rt<<1|1,mid+1,r,x);
	return -1;
}
int n,m,q;
int main(){
	while(~scanf("%d %d",&n,&m)){
		For(i,1,n) scan_d(a[i]);
		build(1,1,n);
		int ansa=0,ansb=0;
		For(i,1,100000){
			if(ansa<n){
				ansb+=m;
				while(1){
					int tt=query(1,1,n,ansb);
					if(tt!=-1) ansb-=a[tt],ansa++,update(1,1,n,tt);
					else break;
				}
			}
			ans1[i]=ansa,ans2[i]=ansb;
		}
		scan_d(q);
		For(i,1,q){
			scan_d(m);
			printf("%d %d\n",ans1[m],ans2[m]);
		}
	}
	return 0;
}

时间:69ms

猜你喜欢

转载自blog.csdn.net/qq_38515845/article/details/82292389
今日推荐