CF1886D Monocarp and the Set

题意

有一个大小为\(n\)的排列,以某种顺序插入一个集合中,给出一个长度为\(n-1\)的字符串,第\(i\)位表示第\(i+1\)个插入的数在已有集合中的情况,\(<\)为最小,\(>\)为最大,\(?\)为其他情况,给出q个单次修改,每次修改后询问其顺序的可能数。

思路

先考虑静态,设当前为str[i],正在插入第\(i+1\)个数,则说明前面有\(i\)个数。若当前位为><,则说明该数在目前唯一确定;若为?,则共有\(i-1\)个空可插入,方案数应\(*(i-1)\)。在\(O(n)\)的时间内可预处理完毕。

考虑动态,因为某一位的情况没有后效性影响,因此可以\(O(1)\)动态维护每次方案,注意有模数,在除法时要使用逆元。

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// Problem: D. Monocarp and the Set
// Contest: Codeforces - Educational Codeforces Round 156 (Rated for Div. 2)
// URL: https://codeforces.com/contest/1886/problem/D
// Memory Limit: 256 MB
// Time Limit: 2000 ms
// Auther : MaxDYF
//
// Powered by CP Editor (https://cpeditor.org)

//#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
const int inf = 1 << 30;
const long long llinf = 1ll << 60;
const double PI = acos(-1);
typedef pair<int, int> pii;
typedef long long ll;
int n, m, k, q;
const ll mod = 998244353ll;
void work()
{
cin >> n >> m;
string a;
cin >> a;
auto pw = [&](ll base, ll x)
{
ll ans = 1;
while (x)
{
if (x & 1)
ans = (ans * base) % mod;
x >>= 1;
base = (base * base) % mod;
}
return ans;
};
ll ans = 1, bj = 1;
for (int i = 0; i < n; i++)
if (a[i] == '?')
{
if (i == 0)
bj = 0;
else
ans = (ans * i) % mod;
}
cout << ans * bj << endl;
char ch;
for (int i = 0, q; i < m; i++)
{
cin >> q >> ch;
if (q == 1 && ch != '?')
bj = 1;
else if (q == 1 && ch == '?')
bj = 0;
else
{
if (a[q - 1] == '?' && ch != '?')
ans = (ans * pw(q - 1, mod - 2) % mod);
else if (a[q - 1] != '?' && ch == '?')
ans = (ans * (q - 1)) % mod;
}
a[q - 1] = ch;
cout << ans * bj << endl;
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
work();
}