题目描述
十滴水是一个非常经典的小游戏。
小 C 正在玩一个一维版本的十滴水游戏。我们通过一个例子描述游戏的基本规则。
游戏在一个 1×c 的网格上进行,格子用整数x(1≤x≤c) 编号,编号从左往右依次递增。网格内 m 个格子里有 1∼41∼4 滴水,其余格子里没有水。在我们的例子中,c=m=5,按照编号顺序,每个格子中分别有 2,4,4,4,22,4,4,4,2 滴水。
玩家可以进行若干次操作,每次操作中,玩家选择一个有水的格子,将格子的水滴数加一。任何时刻若某个格子的水滴数大于等于 55,这个格子里的水滴就会向两侧爆开。此时,这个格子的水被清空,同时对于左方、右方两个方向同时进行以下操作:找到当前格子在对应方向上最近的有水的格子,如果存在这样的格子,将这个格子的水滴数加一。若在某个时刻,有多个格子的水滴数大于等于 55,则最靠左的先爆开。
在我们的例子中,若玩家对第三格进行操作,则其水滴数变为 55,故第三格水滴爆开,水被清空,其左侧最近的有水格子(第二格)和右侧最近的有水格子(第四格)的水量增加 11,此时每个格子中分别有 2,5,0,5,22,5,0,5,2 滴水。
此时第二格和第四格的水滴数均大于等于 55,按照规则,第二格的水先爆开,爆开后每个格子中分别有 3,0,0,6,23,0,0,6,2 滴水;最后第四格的水滴爆开,每个格子中分别有 4,0,0,0,34,0,0,0,3 滴水。
小 C 开始了一局游戏并进行了 n 次操作。小 C 在每次操作后,会等到所有水滴数大于等于 55 的格子里的水滴都爆开再进行下一次操作。
小 C 想知道他的水平有多高,于是他想知道每一次操作后还有多少格子里有水。
保证这 n 次操作都是合法的,即每次操作时操作的格子里都有水。
输入格式
从标准输入读入数据。
输入的第一行三个整数c,m,n 分别表示网格宽度、有水的格子个数以及操作次数。
接下来 m 行每行两个整数x,w,表示第 x 格有 w 滴水。
接下来 n 行每行一个整数 p,表示小 C 对第 p 格做了一次操作。
输出格式
输出到标准输出。
输出 n 行,每行一个整数表示这次操作之后网格上有水的格子数量。
思路
通过读题,可以发现,对一个格子进行操作,增加一滴水,如果爆的话只会影响到左右两个点,左右两个点如果再爆的话又会影响他们的左右点。每个点都是这样,只会影响到左右两个点,也就是前驱和后继。因此,可以考虑用静态链表来处理,记录每个点的前驱和后继。
c的范围很大,1e9,但是m只有3x1e5,而且题目中说了只会选择有水的格子进行操作,也就是在这m个格子里面选。因此,只要存储这m个格子就够了,再排序一下,用pre和nxt记录每个格子的前驱和后继。
然后每次操作,我们把大于等于5的格子放到优先队列里(优先队列:优先爆左,下标的小根堆),把这个格子从链表里删去,再对前驱和后继进行加一滴水,如果大于等于5就加入队列,依次处理队列里的格子就好了。
代码
#include <bits/stdc++.h>
#define N 300005
using namespace std;
int c,n,m,p,vis[N];
map<int,int> idx;
struct node{
int p,w;//位置,水滴数量,
int pre,nxt;//前驱,后继
bool operator < (const node &b) const{//按位置升序
return p<b.p;
}
}a[N];
int main(){
cin>>c>>m>>n;
for(int i=1;i<=m;i++){
cin>>a[i].p>>a[i].w;
}
sort(a+1,a+1+m);
for(int i=1;i<=m;i++){//静态链表预处理
a[i].pre=i-1,a[i].nxt=i+1;
idx[a[i].p]=i;
}
int ans=m;
priority_queue<int,vector<int>,greater<int> > q;
for(int i=1;i<=n;i++){
cin>>p;
int id=idx[p];
a[id].w+=1;//水滴数加1
//用vis标记之前有没有爆过,没有爆且水滴数>=5,就加入队列
if(a[id].w>=5&&!vis[id]) q.push(id),vis[id]=1;
while(q.size()){
ans--;
id=q.top();//最左边的先爆
q.pop();
int pre=a[id].pre,nxt=a[id].nxt;
// cout<<"##"<<id<<" "<<pre<<" "<<nxt<<endl;
a[pre].nxt=nxt;//更新静态链表
a[nxt].pre=pre;
if(pre>=1){//前驱
a[pre].w+=1;
if(a[pre].w>=5&&!vis[pre]) q.push(pre),vis[pre]=1;
}
if(nxt<=m){//后继
a[nxt].w+=1;
if(a[nxt].w>=5&&!vis[nxt]) q.push(nxt),vis[nxt]=1;
}
}
cout<<ans<<endl;
}
return 0;
}