D. Monocarp and the Set
Monocarp has $n$ numbers $1, 2, \dots, n$ and a set (initially empty). He adds his numbers to this set $n$ 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 $n$.
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 $s$ of $n-1$ characters, which represents the characters written out by Monocarp (in the order he wrote them out). You have to process $m$ queries to the string. Each query has the following format:
- $i$ $c$ — replace $s_i$ with the character $c$.
Both before processing the queries and after each query, you have to calculate the number of different ways to order the integers $1, 2, 3, \dots, n$ such that, if Monocarp inserts the integers into the set in that order, he gets the string $s$. Since the answers might be large, print them modulo $998244353$.
Input
The first line contains two integers $n$ and $m$ ($2 \le n \le 3 \cdot 10^5$; $1 \le m \le 3 \cdot 10^5$).
The second line contains the string $s$, consisting of exactly $n-1$ characters <, > and/or ?.
Then $m$ lines follow. Each of them represents a query. Each line contains an integer $i$ and a character $c$ ($1 \le i \le n-1$; $c$ is either <, >, or ?).
Output
Both before processing the queries and after each query, print one integer — the number of ways to order the integers $1, 2, 3, \dots, n$ such that, if Monocarp inserts the integers into the set in that order, he gets the string $s$. Since the answers might be large, print them modulo $998244353$.
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:
- $3, 1, 2, 5, 4, 6$;
- $4, 1, 2, 5, 3, 6$;
- $4, 1, 3, 5, 2, 6$.
After the last query, there is only one possible ordering:
- $3, 5, 4, 6, 2, 1$.
解题思路
这题关键是能想到反过来去考虑问题,即依次删除集合中的元素,这与原问题是等价的。具体来讲就是,假设一开始集合含有元素 $1 \sim n$,那么对于第 $i$ 次的删除操作,分以下三种情况:
- 如果 $s_{n-i} = \text{<}$,那么删除此时集合中最小的元素,只有一种选择。
- 如果 $s_{n-i} = \text{>}$,那么删除此时集合中最大的元素,只有一种选择。
- 如果 $s_{n-i} = \text{?}$,那么删除此时集合中既不是最大也不是最小的元素,此时集合的大小为 $n-i+1$,因此有 $n-i-1$ 种选择。
可以发现每一次的删除操作都是独立的,因为我们并不关心此时集合中具体有哪些元素(满足相对大小即可),只关心集合的大小以及是否删除最值。因此如果 $s_i = \text{?}$,那么就有 $i-1$ 中选择(集合大小为 $i+1$),否则就只有一种选择。所以原始的 $s$ 的合法方案的数量就是 $p = \prod\limits_{\begin{array}{c} i=1 \\ s_i = \text{?} \end{array}}^{n-1}{(i-1)}$。
由于每一次的删除操作都是独立的,因此对于每个询问我们只需在对应的位置修改然后维护 $p$ 即可。可以用线段树来维护区间区间乘积,单点修改。或者用乘法逆元,不过需要注意的是如果 $s_1$ 在修改前是 $\text{?}$,那么我们会出现除以 $0$ 的情况。为此我们可以单独处理 $s_1$,维护一个 $t$,以及维护 $s_2 \sim s_{n-1}$ 对应的乘积 $p$。如果修改后 $s_1 = \text{?}$,则 $t = 0$,否则 $t = 1$,那么每个询问的答案就是 $t \cdot p$。
线段树做法,AC 代码如下,时间复杂度为 $O(m \log{n})$:
#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 代码如下,时间复杂度为 $O(m \log{\operatorname{mod}})$:
#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
标签:Set,Monocarp,int,text,ret,set,mod From: https://www.cnblogs.com/onlyblues/p/17770113.html