D. Monocarp and the Set
D. Monocarp and the Set
Monocarp has numbers and a set (initially empty). He adds his numbers to this set times in some order. During each step, he adds a new number (which has not been present in the set before). In other words, the sequence of added numbers is a permutation of length .
Every time Monocarp adds an element into the set except for the first time, he writes out a character:
- if the element Monocarp is trying to insert becomes the maximum element in the set, Monocarp writes out the character >;
- if the element Monocarp is trying to insert becomes the minimum element in the set, Monocarp writes out the character <;
- if none of the above, Monocarp writes out the character ?.
You are given a string of characters, which represents the characters written out by Monocarp (in the order he wrote them out). You have to process queries to the string. Each query has the following format:
- — replace with the character .
Both before processing the queries and after each query, you have to calculate the number of different ways to order the integers such that, if Monocarp inserts the integers into the set in that order, he gets the string . Since the answers might be large, print them modulo .
Input
The first line contains two integers and (; ).
The second line contains the string , consisting of exactly characters <, > and/or ?.
Then lines follow. Each of them represents a query. Each line contains an integer and a character (; is either <, >, or ?).
Output
Both before processing the queries and after each query, print one integer — the number of ways to order the integers such that, if Monocarp inserts the integers into the set in that order, he gets the string . Since the answers might be large, print them modulo .
Examples
input
6 4
<?>?>
1 ?
4 <
5 <
1 >
output
3
0
0
0
1
input
2 2
>
1 ?
1 <
output
1
0
1
Note
In the first example, there are three possible orderings before all queries:
- ;
- ;
- .
After the last query, there is only one possible ordering:
- .
解题思路
这题关键是能想到反过来去考虑问题,即依次删除集合中的元素,这与原问题是等价的。具体来讲就是,假设一开始集合含有元素 ,那么对于第 次的删除操作,分以下三种情况:
- 如果 ,那么删除此时集合中最小的元素,只有一种选择。
- 如果 ,那么删除此时集合中最大的元素,只有一种选择。
- 如果 ,那么删除此时集合中既不是最大也不是最小的元素,此时集合的大小为 ,因此有 种选择。
可以发现每一次的删除操作都是独立的,因为我们并不关心此时集合中具体有哪些元素(满足相对大小即可),只关心集合的大小以及是否删除最值。因此如果 ,那么就有 中选择(集合大小为 ),否则就只有一种选择。所以原始的 的合法方案的数量就是 。
由于每一次的删除操作都是独立的,因此对于每个询问我们只需在对应的位置修改然后维护 即可。可以用线段树来维护区间区间乘积,单点修改。或者用乘法逆元,不过需要注意的是如果 在修改前是 ,那么我们会出现除以 的情况。为此我们可以单独处理 ,维护一个 ,以及维护 对应的乘积 。如果修改后 ,则 ,否则 ,那么每个询问的答案就是 。
线段树做法,AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 3e5 + 10, mod = 998244353;
char s[N];
struct Node {
int l, r, p;
}tr[N * 4];
void build(int u, int l, int r) {
tr[u] = {l, r};
if (l == r) {
if (s[l] == '?') tr[u].p = l - 1;
else tr[u].p = 1;
}
else {
int mid = l + r >> 1;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
tr[u].p = 1ll * tr[u << 1].p * tr[u << 1 | 1].p % mod;
}
}
void modify(int u, int x, int c) {
if (tr[u].l == tr[u].r) {
tr[u].p = c;
}
else {
if (x <= tr[u].l + tr[u].r >> 1) modify(u << 1, x, c);
else modify(u << 1 | 1, x, c);
tr[u].p = 1ll * tr[u << 1].p * tr[u << 1 | 1].p % mod;
}
}
int main() {
int n, m;
scanf("%d %d %s", &n, &m, s + 1);
build(1, 1, n - 1);
for (int i = 0; i < m + 1; i++) {
printf("%d\n", tr[1].p);
int x;
char c[5];
scanf("%d %s", &x, c);
if (c[0] == '?') modify(1, x, x - 1);
else modify(1, x, 1);
}
return 0;
}
乘法逆元做法,AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 3e5 + 10, mod = 998244353;
char s[N];
int qmi(int a, int k) {
int ret = 1;
while (k) {
if (k & 1) ret = 1ll * ret * a % mod;
a = 1ll * a * a % mod;
k >>= 1;
}
return ret;
}
int main() {
int n, m;
scanf("%d %d %s", &n, &m, s + 1);
int ret = 1;
for (int i = 2; i < n; i++) {
if (s[i] == '?') ret = ret * (i - 1ll) % mod;
}
for (int i = 0, t = s[1] != '?'; i < m + 1; i++) {
printf("%d\n", ret * t);
int x;
char c[5];
scanf("%d %s", &x, c);
if (x == 1) {
t = c[0] != '?';
}
else {
if (s[x] == '?') ret = 1ll * ret * qmi(x - 1, mod - 2) % mod;
if (c[0] == '?') ret = ret * (x - 1ll) % mod;
}
s[x] = c[0];
}
return 0;
}
参考资料
Educational Codeforces Round 156 Editorial:https://codeforces.com/blog/entry/121255
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/17770113.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效