题解:P9661 [ICPC2021 Macao R] Sandpile on Clique

P9961 题解

题面

原题传送门

题意

给定一张 \(n\) 个点的完全图,每个点有点权 \(a_i\),每一次要选一个点满足 \(a_i>=n-1\),然后令其他点的点权加一,这个点权减去 \(n-1\),问操作若干次后无法操作时点权分别是多少,如果不会出现操作不了就输出 Recurrent

思路

其实就是一道非常非常简单的模拟题。。。

当然,要动点脑筋。(不然怎么是绿。)

首先是判断无解,可以考虑把所有大于等于 \(n-1\) 的部分全部取出来,最后再把这一部分贡献加回去。(注意这里取出贡献的点也要减一,这样就不用麻烦的判断贡献出自哪里,下面模拟也一样。)此时的点权排序一下若有一个小于 \(i-1\) 那么就说明这一定是有解的,正确性显然。

接下来就是模拟,懂点小脑筋,考虑每个大于等于 \(n-1\) 的点不要把贡献直接加给别的点,而是将每个点会产生的贡献加起来,最后统计答案的时候再加上,不然就会大大增加时间复杂度,因为在模拟的时候已经保证了一定不会不会操作不了,所以可以放心大胆的利用优先队列(从大到小排序)模拟。

于是,你就过了这道题。

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#define ll long long
#define pll pair<ll,ll>
using namespace std;
const int MN=5e5+5;
ll n,a[MN],b[MN],cnt,ans[MN];
priority_queue<pll> q;
void write(ll n){if(n<0){putchar('-');write(-n);return;}if(n>9)write(n/10);putchar(n%10+'0');}
ll read(){ll x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
bool pd(){//判断无解
    cnt=0;
    for(int i=1; i<=n; i++) b[i]=a[i];
    for(int i=1; i<=n; i++){
        ll num=b[i]/(n-1);
        b[i]%=(n-1);b[i]-=num;cnt+=num;
    }
    for(int i=1; i<=n; i++) b[i]+=cnt;
    sort(b+1,b+1+n);cnt=0;
    for(int i=1; i<=n; i++) if(b[i]<i-1) return false;
    return true;
}
int main(){
    n=read();
    for(int i=1; i<=n; i++) a[i]=read();
    if(pd()){printf("Recurrent\n");return 0;}
    for(int i=1; i<=n; i++) q.push({a[i],i});
    while(q.top().first+cnt>=n-1){
        pll tmp=q.top();q.pop();cnt++;
        q.push({tmp.first-n,tmp.second});
    }
    while(!q.empty()){
        pll tmp=q.top();q.pop();
        ans[tmp.second]=tmp.first+cnt;
    }
    for(int i=1; i<=n; i++) write(ans[i]),putchar(' ');
    return 0;
}
posted @ 2025-01-29 16:03  naroto2022  阅读(16)  评论(0)    收藏  举报