题目大意:有n个房间,第i个房间内有ki盏白炽灯。现在准备将这些白炽灯全部换成节能灯。管家每个月购买m盏节能灯,之后会按照房间列表一间一间地查看是否需要更换。当管家遇见一个尚未更换节能灯的房间,并且他手上有足够多的节能灯时,他会将房间内的所有白炽灯全部替换为节能灯。不管他路过这个房间后是否换了灯,他都会继续前往下一个房间。如果他走过所有房间之后手中仍有多余的节能灯,他将会把这些节能灯留到下个月继续使用。你的任务是对于给出的询问d,求出d个月后有多少房间已经换好了节能灯,以及管家手上有多少盏节能灯。
输入:第一行有两个数字,分别是n(1<=n<=100000)和m(1<=m<=100);第二行有n个数字,第i个数字ki(1<=ki<=10000)表示第i个房间有多少盏白炽灯;第三行有一个数字q(1<=q<=100000),表示一共有q组询问;第四行有q个数字,第i个数字di(1<=di<=100000)表示第i组询问所询问的月份。
输出:一共q行,每行两个数字,分别表示当月有多少房间已经换好灯以及管家手上仍有多少灯。
题解:
一道比较简单的利用线段树维护的模拟题。
首先考虑暴力模拟,记录下管家当前手中有多少白炽灯,然后一个月一个月地跑,用数组记录下每个月的答案(因为di<=100000所以开得下数组)。但是这个写法效率上无法承受——极端情况下效率可以达到O(nd),也就是10的10次方。注意到每个房间只会被换一次灯,每轮模拟跑全部房间没有必要。于是考虑采用线段树维护,线段树中记录下房间灯数的最小值。每一轮模拟的时候,找出管家当前位置到结尾为止的房间中,最近的一个可以换灯的房间。具体操作为,在线段树中查询时,如果左子树的最小值小于等于当前灯数,就到左子树中继续寻找,否则考虑右子树。找到之后,更新答案,并将这个节点的值设为INF,在回溯的时候更新线段树。如果所有节点的最小值都比当前灯数要大,则说明这一轮模拟结束,进入下一轮。这样的话,一共只跑了总月份数加上总房间数再乘以log2(n),可以通过。
(考场上看错题,以为是暴力模拟,WA了两发之后心态炸了,脑出解法之后扔给同学码了……因此底下的代码不是我写的。)
代码:
//code by redwind
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<iomanip>
#include<stack>
#include<map>
#include<set>
#include<cmath>
#define debug(x) cerr<<#x<<"="<<x<<endl
#define INF 0x7f7f7f7f
using namespace std;
typedef pair<int,int> pii;
typedef long long ll;
inline int init()
{
int now=0,ju=1;char c;bool flag=false;
while(1)
{
c=getchar();
if(c=='-')ju=-1;
else if(c>='0'&&c<='9')
{
now=now*10+c-'0';
flag=true;
}
else if(flag)return now*ju;
}
}
inline long long llinit()
{
long long now=0,ju=1;char c;bool flag=false;
while(1)
{
c=getchar();
if(c=='-')ju=-1;
else if(c>='0'&&c<='9')
{
now=now*10+c-'0';
flag=true;
}
else if(flag)return now*ju;
}
}
#define lson (now<<1)
#define rson (now<<1|1)
#define mid ((l+r)>>1)
class node
{
public:
int minx;
}tree[2000005];
int a[2000005],n,m;
int ans1[2000005],ans2[2000005];
int cnt=0;
inline void update(int now)
{
tree[now].minx=min(tree[lson].minx,tree[rson].minx);
}
inline void Build_Tree(int l,int r,int now)
{
if(l==r)
{
tree[now].minx=a[l];
return;
}
else
{
Build_Tree(l,mid,lson);
Build_Tree(mid+1,r,rson);
update(now);
}
}
inline void Modify_Tree(int l,int r,int pos,int now)
{
if(l==r)
{
tree[now].minx=INF;
return;
}
else
{
if(pos<=mid)Modify_Tree(l,mid,pos,lson);
else if(pos>mid)Modify_Tree(mid+1,r,pos,rson);
update(now);
}
}
inline int Query_Tree(int l,int r,int now,int x)
{
if(l==r)
{
return l;
}
else
{
if(tree[lson].minx<=x)
{
return Query_Tree(l,mid,lson,x);
}
else if(tree[rson].minx<=x)
{
return Query_Tree(mid+1,r,rson,x);
}
else return -1;
}
}
int main()
{
n=init();m=init();
int tmp=0,c=0,pos;
for(int i=1;i<=n;i++)
{
a[i]=init();
}
Build_Tree(1,n,1);
while(cnt!=n&&c!=100000)
{
c++;tmp+=m;
while(pos=Query_Tree(1,n,1,tmp),pos!=-1)
{
tmp-=a[pos];
Modify_Tree(1,n,pos,1);
cnt++;
}
ans1[c]=cnt;ans2[c]=tmp;
}
int t,q;q=init();
for(int i=1;i<=q;i++)
{
t=init();if(t>c)t=c;
printf("%d %d\n",ans1[t],ans2[t]);
}
return 0;
}