南京网络赛 - G
Lpl and Energy-saving Lamps
During tea-drinking, princess, amongst other things, asked why has such a good-natured and cute Dragon imprisoned Lpl in the Castle? Dragon smiled enigmatically and answered that it is a big secret. After a pause, Dragon added:
— We have a contract. A rental agreement. He always works all day long. He likes silence. Besides that, there are many more advantages of living here in the Castle. Say, it is easy to justify a missed call: a phone ring can’t reach the other side of the Castle from where the phone has been left. So, the imprisonment is just a tale. Actually, he thinks about everything. He is smart. For instance, he started replacing incandescent lamps with energy-saving lamps in the whole Castle…
Lpl chose a model of energy-saving lamps and started the replacement as described below. He numbered all rooms in the Castle and counted how many lamps in each room he needs to replace.
At the beginning of each month, Lpl buys mm energy-saving lamps and replaces lamps in rooms according to his list. He starts from the first room in his list. If the lamps in this room are not replaced yet and Lpl has enough energy-saving lamps to replace all lamps, then he replaces all ones and takes the room out from the list. Otherwise, he’ll just skip it and check the next room in his list. This process repeats until he has no energy-saving lamps or he has checked all rooms in his list. If he still has some energy-saving lamps after he has checked all rooms in his list, he’ll save the rest of energy-saving lamps for the next month.
As soon as all the work is done, he ceases buying new lamps. They are very high quality and have a very long-life cycle.
Your task is for a given number of month and descriptions of rooms to compute in how many rooms the old lamps will be replaced with energy-saving ones and how many energy-saving lamps will remain by the end of each month.
Input
Each input will consist of a single test case.
The first line contains integers n and m (1≤n≤100000,1≤m≤100) — the number of rooms in the Castle and the number of energy-saving lamps, which Lpl buys monthly.
The second line contains nn integers k1, k2, …, kn(1≤kj≤10000,j=1,2,…,n) — the number of lamps in the rooms of the Castle. The number in position j is the number of lamps in j-th room. Room numbers are given in accordance with Lpl’s list.
The third line contains one integer q(1≤q≤100000) — the number of queries.
The fourth line contains qq integers d1, d2, …, dq(1≤dp≤100000,p=1,2,…,q) — numbers of months, in which queries are formed.
Months are numbered starting with 1; at the beginning of the first month Lpl buys the first m energy-saving lamps.
Output
Print q lines.
Line pp contains two integers — the number of rooms, in which all old lamps are replaced already, and the number of remaining energy-saving lamps by the end of dp month.
Hint
Explanation for the sample:
In the first month, he bought 4 energy-saving lamps and he replaced the first room in his list and remove it. And then he had 1 energy-saving lamps and skipped all rooms next. So, the answer for the first month is 1,1------1 room’s lamps were replaced already, 1 energy-saving lamp remain.
样例输入
5 4
3 10 5 2 7
10
5 1 4 8 7 2 3 6 4 7
样例输出
4 0
1 1
3 6
5 1
5 1
2 0
3 2
4 4
3 6
5 1
分析
这个题目一开始看根本没想到是线段树,看了网上的题解才有了思路。问题是这样的,有n个房间,每个房间有ki个旧灯泡,每个月能得到m个新灯泡,每次从左往右找小于当前新灯泡数的房间,换上ki个新灯泡,并且不在找这个房间,每次都只从左到右找一次,一次可以换多个房间,多余的灯泡留到下个月。如果所有房间都换完了,就不在得到新灯泡了。
实际上可以这样思考,我每次都找到从左往右第一个小于当前灯泡k的房间,然后给他换灯泡,在一个月中不断重复上述操作,直到找不到为止。因为房间数是固定的,所以最多找房间的次数就是房间的个数1e5,最多的找不到的次数是询问的次数,也是1e5,所以总共的时间是2e5,然后找第一个小于k的房间用线段树,总的时间复杂度是O(nlgn),可以很快解决。
至于用线段树求小于k的房间,很简单,只要修改询问的地方。首先需要维护的是区间最小值,因为要找到第一个小于k的房间,所以我们只要优先询问左区间,如果左区间的最小值小于k则询问左区间,否则就询问右区间,因为左区间不存在小于k的数。如果找到单点就直接返回位置就行了。
参考代码
#include <cstdio>
#include <iostream>
#include <algorithm>
#define MAXN 100005
#define left rt<<1
#define right rt<<1|1
const int inf=0x3f3f3f3f;
using namespace std;int n,m,q,d;
int k[MAXN];struct SegTree {
int l,r,Min;
}t[MAXN<<2];// 给答案打表
struct node {
int room; int lamp;
}ans[MAXN];inline void pushup(int rt) {
t[rt].Min=min(t[left].Min,t[right].Min);
}void build(int l,int r,int rt) {
t[rt].l=l; t[rt].r=r;if (l==r) {
t[rt].Min=k[l];return;}int m=(l+r)>>1;build(l,m,left);build(m+1,r,right);pushup(rt);
}void update(int p,int c,int rt) {
if (t[rt].l==t[rt].r) {
t[rt].Min=c;return;}int m=(t[rt].l+t[rt].r)>>1;if (p<=m) update(p,c,left);if (p>m) update(p,c,right);pushup(rt);
}// 唯一需要修改的地方
int query(int rt,int k) {
// 查询到单点,则该点就是我们要找的点,返回位置if (t[rt].l==t[rt].r) {
return t[rt].l;}// 此处只有左区间查询不到的时候才查询右区间if (t[left].Min<=k) return query(left,k);else if (t[right].Min<=k) return query(right,k);// 没有比k小的返回0return 0;
}int main() {
scanf("%d%d",&n,&m);for (int i=1;i<=n;i++)scanf("%d",&k[i]);build(1,n,1);scanf("%d",&q);// 答案打表int lamp=0,room=0,mon=MAXN;for (int i=1;i<MAXN;i++) {
// 所有房间已经换完就break,这里居然还是直接跑到MAXN快if (room>=n) {
mon=i;ans[i].room=room;ans[i].lamp=lamp;break;}lamp+=m;// 不停换灯泡直到换不了了while(1) {
int pos=query(1,lamp);if (pos==0) break;lamp-=k[pos];// 换完就给当前灯泡置为infupdate(pos,inf,1);room+=1;}ans[i].room=room;ans[i].lamp=lamp;}for (int i=1;i<=q;i++) {
scanf("%d",&d);if (d>mon) d=mon;printf("%d %d\n",ans[d].room,ans[d].lamp);}return 0;
}