南外集训 2024.2.23 T3

Kubic 素质如此,如何国家队?

有一个初始为空的序列,对其进行 q 次操作(强制在线)。操作分为两种:

  • 1x 表示在序列末尾插入一个 x

  • 2x 表示询问当前序列中长度等于 x 的区间的价值之和 mod998244353

定义一个区间的价值为中前 m 大的数的乘积,其中 m 是给定的常数。如果区间长度不到 m 则其价值为区间中所有数的乘积。

保证 1 操作个数恰好为给定常数 n,且任意时刻序列中所有数互不相同。

数据范围:1n105,1m200,nq2×105

首先暴力跳链表是经典的套路(所谓的 EX 单调栈),似乎还考过,就不赘述。理解一下题解中说的这个 b 的维护。也就是在末尾加入一个数的时候,有一些位置对应的区间的最大 m 个元素集合会发生变化,这个时候我们需要重新计算这个位置的权值,所以我们需要时刻知道一个关键点对应的区间的第 m 大元素是什么,将其记作 bp

被重新计算的一定是被扫描到的元素,这保证这一部分的复杂度均摊正确。我们插入的数记为 w。最后 m 个位置的维护是平凡的。对于其他位置,记 x,y 是相邻的两个关键点,x 在前面,不妨认为 x 存在且被扫描到;如果不存在意味着 y 是最后一个被扫描的元素,此时不妨暴力扫前面的所有关键位置,用 nth_element 求第 m1 小,复杂度均摊正确。

于是我们扫描到了 xx 没有被删除,那么不难发现 byw,否则在当前的 x 这个位置,wax 中会有一个被踢出,矛盾。于是 by 一定是原先 y 对应集合中第 m1 大的元素。注意到 x 是关键位置,所以 ax 一定被成功插入 y 对应的集合中,并踢出了其中第 m 大的元素。此时有两种情况:要么 ax=bx,要么 bx 是就是 y 集合中第 m1 大的元素。考虑前者:注意到 w 一定被成功地插入到 x 的集合中;假如 x 也不是最后一个被扫描到的元素,那么它的集合内新的第 m 大元素不是 w,于是自然就是 y 的第 m1 大;否则我们会暴力扫描获得 x 的集合。我们不妨求出其中不包含 w 的第 m1 大,这样可以求出它的新的第 m 大,又可以向后一个位置传递信息。这一部分就做完了,总复杂度 Θ(nm)

考虑维护答案的部分。把区间 [l,r] 放到笛卡尔坐标系上 (l,r) 这个点上,那么每次询问要求的是一条斜线 y=x+d 上所有点的权值和。每次修改会给某一列上的一个区间加某个数 {(x,r)x[a,b]},而没有被修改的点都会继承原来的值,也就是说修改的实际上是矩形 {(x,y)x[a,b],yr}。考虑询问与修改的交:对于 y 这一列要求 d[yb,ya]。把修改改写,挂到点 (yb,ba)=(p,q) 上,这些点构成平面上的一条从 (rb,ba) 开始向右无限延伸的横线,那么当前询问要求 d[p,p+q],即 pd,qdp,相当于在一条竖线和一条斜向下直线的左上方。画出图观察容易发现只需要计算一条竖线左边的总贡献减掉一个左下角直角三角形的贡献,使用 Θ(1)Θ(n) 的分块来维护即可。

代码如下(最劣解取之 被艾伦赵打败了我去)。注意询问时要减掉最后 d 个区间的代价和,这是多算的。

// Author: kyEEcccccc
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
using ULL = unsigned long long;
#define F(i, l, r) for (int i = (l); i <= (r); ++i)
#define FF(i, r, l) for (int i = (r); i >= (l); --i)
#define MAX(a, b) ((a) = max(a, b))
#define MIN(a, b) ((a) = min(a, b))
#define SZ(a) ((int)((a).size()) - 1)
constexpr int N = 100005, B = 330, MOD = 998244353;
LL kpow(LL x, ULL k = MOD - 2)
{
x = x % MOD;
LL r = 1;
while (k)
{
if (k & 1) r = r * x % MOD;
x = x * x % MOD;
k >>= 1;
}
return r;
}
int typ;
int n, nn, m, q;
int a[N], b[N], lv[N];
int inv[10000005];
LL val[N];
int nxt[N], hd;
int nb[N];
struct Blocks
{
LL val[N], valp[N];
LL sum[N / B + 5], sump[N / B + 5];
void add(int pos, LL x)
{
// assert(pos >= 0 && pos <= nn);
x %= MOD;
(val[pos] += x) %= MOD;
(sum[pos / B] += x) %= MOD;
(valp[pos] += x * pos) %= MOD;
(sump[pos / B] += x * pos) %= MOD;
}
array<LL, 2> get_sum(int pos)
{
// assert(pos >= 0);
LL x = 0, y = 0;
F(i, 0, pos / B - 1)
{
x += sum[i];
y += sump[i];
}
F(i, pos / B * B, pos)
{
x += val[i];
y += valp[i];
}
return {x % MOD, y % MOD};
}
} blka, blkb, blkc;
void add(int a, int b, int r, LL x)
{
// cerr << "perf: " << a << ' ' << b << ' ' << r << ' ' << x << '\n';
blka.add(r - b, x);
blkb.add(r - a, x);
blkc.add(nn - (a - 1), -x);
blkc.add(nn - b, x);
}
int vec[N], tv, cv[N];
signed main(void)
{
freopen("formalized.in", "r", stdin);
freopen("formalized.out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(nullptr);
inv[0] = 1;
F(i, 1, 10000000) inv[i] = (LL)inv[i - 1] * i % MOD;
inv[10000000] = kpow(inv[10000000]);
FF(i, 10000000, 1) inv[i - 1] = (LL)inv[i] * i % MOD;
LL pd = 1;
F(i, 1, 10000000)
{
inv[i] = inv[i] * pd % MOD;
(pd *= i) %= MOD;
}
// F(i, 1, 10000000) assert((LL)inv[i] * i % MOD == 1);
cin >> typ >> n >> m >> q;
nn = n;
n = 0;
int lst_ans = 0;
hd = 0;
pd = 0;
F(_, 1, q)
{
// cerr << _ << ":" << '\n';
int op, d;
cin >> op >> d;
d ^= lst_ans * typ;
if (op == 1)
{
(pd += d) %= MOD;
++n;
// assert(n <= nn);
a[n] = d;
lv[n] = 0;
b[n] = a[n];
val[n] = 1;
nxt[n] = hd;
hd = n;
int p = n;
tv = 0;
int ct = 0;
while (1)
{
if (nxt[p] == 0)
{
vec[++tv] = p;
break;
}
if (a[nxt[p]] < a[n])
{
++lv[nxt[p]];
if (lv[nxt[p]] == m)
{
add(nxt[nxt[p]] + 1, nxt[p], n, -val[nxt[p]]);
// cerr << "! " << nxt[p] << '\n';
nxt[p] = nxt[nxt[p]];
}
else
{
vec[++tv] = p;
p = nxt[p];
add(nxt[p] + 1, p, n, -val[p]);
}
}
else
{
++ct;
vec[++tv] = p;
if (ct == m) break;
p = nxt[p];
add(nxt[p] + 1, p, n, -val[p]);
}
}
if (m == 1)
{
val[n] = a[n];
add(nxt[n] + 1, n, n, val[n]);
}
else if (n <= m)
{
F(i, 1, tv)
{
int x = vec[i];
MIN(b[x], a[n]);
(val[x] *= a[n]) %= MOD;
add(nxt[x] + 1, x, n, val[x]);
}
}
else
{
F(i, 1, tv - 1) cv[i] = a[vec[i + 1]];
nth_element(cv + 1, cv + tv - (m - 1), cv + tv);
nb[vec[tv]] = cv[tv - (m - 1)];
FF(i, tv - 1, 1)
{
if (n - vec[i] + 1 <= m) nb[vec[i]] = min(b[vec[i]], a[n]);
else if (a[vec[i + 1]] == b[vec[i + 1]]) nb[vec[i]] = nb[vec[i + 1]];
else nb[vec[i]] = b[vec[i + 1]];
}
MIN(nb[vec[tv]], a[n]);
F(i, 1, tv)
{
int x = vec[i];
if (n - x + 1 > m) (val[x] *= inv[b[x]]) %= MOD;
(val[x] *= a[n]) %= MOD;
add(nxt[x] + 1, x, n, val[x]);
b[x] = nb[x];
}
}
}
else
{
--d;
if (d == 0) lst_ans = pd;
else if (d >= n) lst_ans = 0;
else
{
auto ra = blka.get_sum(d), rb = blkb.get_sum(d - 1),
rc = blkc.get_sum(nn - (n - d + 1));
lst_ans = (((ra[0] * (d + 1) - ra[1]) - (rb[0] * d - rb[1])
- (rc[0] * (nn - (n - d + 1) + 1) - rc[1])) % MOD + MOD) % MOD;
}
cout << lst_ans << '\n';
}
}
return 0;
}
posted @   kyEEcccccc  阅读(322)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示