NOIP2023模拟8联测29 B. 差后队列

NOIP2023模拟8联测29 B. 差后队列

题目大意

差后队列为一种数据结构,支持两种操作:

  • push 插入一个数
  • pop 随机删除一个 不是 最大值的数。如果只有一个数则删除该数

给定操作序列,求每次删的数的期望,以及每个数期望被删的时间,答案 m o d    998244353 \mod 998244353 mod998244353

思路

题解居然说这是签到题

对于每次要删的数的期望。

每次从队列没有数开始单独处理。

  • 如果当前只有一个数

    那么直接删除就好了

  • 否则

    维护一个当前的最大值和当前数的数量,每次的数的期望就相当于对于每个在队列内的非最大值的数 n u m num num 乘上之前没被删掉的期望,再乘上这一次被删掉的期望。

对于每次数被删除的数的期望时间

维护一个类似于后缀和的东西。

从后往前开始,一个数的期望删除的时间就是这次被删除的期望加上这次没有被删除的期望乘上以后被删除的期望。

code

#include <bits/stdc++.h>
#define LL long long
#define fu(x , y , z) for(int x = y ; x <= z ; x ++)
#define fd(x , y , z) for(int x = y ; x >= z ; x --)
using namespace std;
const int N = 1e6 + 5;
const LL mod = 998244353;
int n , op[N];
LL x[N] , ans[N] , inv[N] , max1[N] , p[N] , tans[N]; 
LL ksm (LL x , LL y) {
    if (!y) return 1;
    LL z = ksm (x , y / 2);
    z = z * z % mod;
    if (y & 1) z = z * x % mod;
    return z; 
}
int main () {
    freopen ("queue.in" , "r" , stdin);
    freopen ("queue.out" , "w" , stdout);
    int y;
    scanf ("%d" , &n);
    fu (i , 1 , n) {
        scanf ("%d" , &op[i]);
        if (op[i] == 0) scanf ("%lld" , &x[i]);
    }
    inv[0] = inv[1] = 1;
    fu (i , 2 , n) inv[i] = ksm (i , mod - 2);
    int cnt = 0;
    LL now = 0;
    fu (i , 1 , n) {
        max1[i] = max (max1[i - 1] , x[i]);
        if (op[i] == 0) {
            cnt ++;
            if (x[i] > max1[i - 1]) 
                now = (now + max1[i - 1]) % mod;
            else 
                now = (now + x[i]) % mod;
        }
        else {
            if (cnt == 1) {
                ans[i] = max1[i];
                max1[i] = 0;
                p[i] = 1;
            }
            else {
                p[i] = inv[cnt - 1];
                ans[i] = now * p[i] % mod;
                now = now * (1ll - p[i] + mod) % mod;
            }
            cnt --;
        }
    }
    // fu (i , 1 , n) cout << ans[i] << " ";
    // return 0;
    now = cnt = 0;
    LL tmp = 0;
    fd (i , n , 1) {
        if (op[i] == 1) {
            tmp = tmp * (1ll - p[i] + mod) % mod;
            tmp = (tmp + i * p[i]) % mod;
        }
        tans[i] = tmp;
    }
    // fu (i , 1 , n) cout << tans[i] << " ";
    // return 0;
    fu (i , 1 , n) {
        if (op[i] == 0) {
            fu (j , i , n) {
                if (max1[j] != x[i]) {
                    ans[i] = tans[j];
                    break;
                }
            }
        }
    }
    fu (i , 1 , n) printf ("%lld " , ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值