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;
}