题意:n间房子,由1~n编号,每个房间里有若干旧灯泡,每个月能获得m个节能灯泡,从编号为1的房间开始换灯泡,如果手中的节能灯泡能将该房间内的所有旧灯泡都换掉就将旧灯泡全部换成节能灯泡,并且下一个月就不需要再换该房间了;如果手中的灯泡不够换全部的旧灯泡,就跳过这个房间,换下一个房间,知道手中无灯泡或经过了所有的房子,如果经过所有房子后手中还有灯泡,就留到下一个月用,即下个月初手中节能灯泡数为m+上月剩余;如果所有房间的灯泡都换完了,下个月就不再获得节能灯泡;题目共Q个询问,问第qi个月结束后已经换完灯泡的房间数和该月结束时手中剩余灯泡数;
比赛时看着这个题出的不多也就没看,赛后霸体看了一遍,发现,这么简单!!!早知道不去死扣最后那个题了!!!!!算是长个教训,下次绝不再一个题上吊死!!!
思路:线段树的每个节点存下该区间的len,sum,min;当遇到区间sum<=val时就可以将整个区间的房间换灯泡,房间数就是区间的len,换完后由于下次无需再换就将sum置为INF,min置为INF,遇到区间的min>val的就直接跳过;这样先找左孩子,再找右孩子,返回时要返回剩余灯泡数; 然后从1月开始一直到询问的最大月;每个月都更新一遍;最后对于对应月份按询问顺序输出就可以了;
我的代码有点复杂了,其实不用对询问排序,用个结构体数组存下每个月的答案,然后对应输出就ok;代码就多了个排序,就不再改了~~~
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=1e5+10;
struct node{
int l, r, sum, len, minn;
}tr[maxn<<2];
void pushup(int m){
tr[m].sum=min(tr[m<<1].sum+tr[m<<1|1].sum, INF);
tr[m].minn=min(tr[m<<1].minn, tr[m<<1|1].minn);
}
void build(int m, int l, int r){
tr[m].l=l;
tr[m].r=r;
tr[m].len=r-l+1;
if(l==r){
scanf("%d", &tr[m].sum);
tr[m].minn=tr[m].sum;
return;
}
int mid=(l+r)>>1;
build(m<<1, l, mid);
build(m<<1|1, mid+1, r);
pushup(m);
}
int cnt;
int updata(int m, int val){
if(tr[m].minn>val){
return val;
}
if(tr[m].sum<=val){
cnt+=tr[m].len;
val-=tr[m].sum;
tr[m].sum=INF;
tr[m].minn=INF;
return val;
}
if(tr[m].l==tr[m].r){
return val;
}
int mid=(tr[m].l+tr[m].r)>>1;
int temp=updata(m<<1, val);
temp=updata(m<<1|1, temp);
pushup(m);
return temp;
}
struct query{
int id, month, cnt, re;
}q[maxn];
bool cmp1(query a, query b){
return a.month<b.month;
}
bool cmp2(query a, query b){
return a.id<b.id;
}
int main(){
int n, m;
scanf("%d%d", &n, &m);
build(1, 1, n);
int Q;
scanf("%d", &Q);
for(int i=0; i<Q; i++){
scanf("%d", &q[i].month);
q[i].id=i;
}
sort(q, q+Q, cmp1);
int energy=0;
int month=0;
for(int i=0; i<Q; i++){
while(month<q[i].month){
if(cnt<n)
energy+=m;
energy=updata(1, energy);
month++;
}
q[i].cnt=cnt;
q[i].re=energy;
}
sort(q, q+Q, cmp2);
for(int i=0; i<Q; i++){
printf("%d %d\n", q[i].cnt, q[i].re);
}
return 0;
}