【CCF-CSP】202403-4 十滴水

借鉴自:【CCF-CSP】202403-4 十滴水-CSDN博客

题目描述
十滴水是一个非常经典的小游戏。

小 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 想知道他的水平有多高,于是他想知道每一次操作后还有多少格子里有水。

题解:

#include<bits/stdc++.h>
#define int long long
#define up(l,r,i) for(int i=l,END##i=r;i<=END##i;++i)
#define dn(r,l,i) for(int i=r,END##i=l;i>=END##i;--i)
using namespace std;
#define IOS ios::sync_with_stdio("false");cin.tie(0);cout.tie(0);
#define freopen freopen("IN.in","r",stdin);freopen("OUT.out","w",stdout);
typedef pair<int,int> PII;
const int N=3e5+10;
const int INF=0x7ffffff;
struct node
{
    int p,w;
    int pre,nex;
    bool operator< (const node & oth) const{
        return p<oth.p;
    }
}a[N];
map<int,int> idx;
bool vis[N];
signed main(){
    IOS

    int c,m,n;
    cin>>c>>m>>n;
    int ans=m;
    for(int i=1;i<=m;i++){
        cin>>a[i].p>>a[i].w;
    }
    sort(a+1,a+m+1);
    for(int i=1;i<=m;i++){
        a[i].pre=i-1;
        a[i].nex=i+1;
        idx[a[i].p]=i;
    }
    priority_queue<int,vector<int>,greater<int> > q;
    for(int i=1;i<=n;i++){
        int x;
        cin>>x;
        int id=idx[x];
        a[id].w+=1;
        if(a[id].w>=5&&!vis[id]){
            q.push(id);
            vis[id]=true;
        }
        while(!q.empty()){
            ans--;
            id=q.top();
            q.pop();
            int pre=a[id].pre,nxt=a[id].nex;
            a[pre].nex=nxt;
            a[nxt].pre=pre;
            if(pre>=1){
                a[pre].w+=1;
                if(a[pre].w>=5&&!vis[pre]){
                    q.push(pre);
                    vis[pre]=true;
                }
            }
            if(nxt<=m){
                a[nxt].w+=1;
                if(a[nxt].w>=5&&!vis[nxt]){
                    q.push(nxt);
                    vis[nxt]=true;
                }
            }
            
        }
        cout<<ans<<endl;
    }
    return 0;
}

这道题难点在于点的范围是1e9的,但是仅有3e5+5个点。因此可以使用离散化来描述点的信息。每个点可以用位置,水滴数以及左边最近有水的格子(前驱pre),右边最近有水的格子(后驱(nex))来描述。每次操作之后将会爆开的点存入优先队列当中,由于是左边的点会先爆开,因此使用小根堆来存即可。

  • 8
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值