把过去和现在的笔记记录也搬了过来,也算是给以后留个念想吧,想想一开始打acm就是图一乐,后来发现这游戏还挺上头的,也算是度过了一段电竞生涯(xs)
早些时候的笔记写的好中二,连我自己看着都羞耻。
不过,就喜欢这种羞耻的感觉。
收录的题目大部分是个人认为质量不错的题目,以DP为主,非DP的题目都用※进行了标识。
当然,有些题解思路本身也是源自其他人的,不过除非特殊标注,否则都是用的自己的代码。

题目大意:

CF1613D Mex Sequences
题目大意,给一个数组a,长度范围在5e5内。定义一个性质MEX-correct:对于序列中的每一个数xi,都有abs(xi-MEX(x0,x1,…,xi))<=1,求数组a满足这个性质的子序列个数,取模998244353。

解:

一眼5e5,个数,取模,线性dp,然后不会了,开摆。
MEX的题也做过了,这题中很容易想到,对于一个满足Mc性质(简写了)的序列中的数xi,当时的MEX值要么等于xi+1要么等于xi-1,因为MEX不可能等于本身,如果MEX值只能等于xi-1时很好办,我们只要去从下往上线性dp把数量计算出来,使得序列最终包含0-xi的数(加上xi本身之后)就完了。而第二种情况实际上也很好算,我们只需要算序列包含0-(xi-1)的个数就行,因为它是个特例,为什么这么说呢?因为一旦出现了这种情况,MEX的值成为了xi-1,在这个序列的后面要是再想添加数,就只能添加这个数本身或者本身减二(最麻烦的地方),其他任何数都会出现错误,所以它没有后效,只要作为特例处理就行。
于是我们设计一个dp[i][j][2],表示最远涉及到第i个数的结尾j的Mc序列数量,后面的标记则是表示属于哪种情况,对于第一种情况实际上我们求的是一个不降子序列,第二种情况则是叠加。由于实际上我们不需要求最长的子序列,只要求数量,所以只要顺序遍历即可,第一维大可以不必,复杂度为O(n)。
状态转移:
dp[a[i]][1]+=dp[a[i]][1]+dp[a[i]-1][1],
dp[a[i]][2]+=dp[a[i]][1]+dp[a[i]][2],
dp[a[i]+2][2]+=dp[a[i]+2][2](这步很关键,千万不能少,而且注意是加在a[i]+2上而不是a[i],因为在这里第二个方程和第三个并不完全一样,第二个是从第一种情况转化成第二种情况,第三个则是一开始就在第二种情况,所以它是被当作以a[i]+2为结尾的序列看待的);

这题还是蛮难的,从各种程度上来讲,细节也很多,需要很熟练这种计数方法,甚至最后还要卡你个memset,不愧1900之名好吧……
不过2000分以下的线性DP的极限也就到这了呢(笑

代码(代码还挺简洁的……这里我之前写的代码中dp1和dp2对应题解中的dp[][1]和dp[][2]):

代码

代码
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

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <algorithm>
#include <utility>
#include <vector>
#include <istream>
#include <map>
#include <cmath>
#include <set>
#include <cstring>
#include <string>
#define ll long long
#define maxn 500005
#define mdl 998244353
#define clr(a,n) for(int i=0;i<n;i++)a[i]=0
#define cfast std::ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
ll dp1[maxn], dp2[maxn], a[maxn];
int main()
{
cfast;
int t;
cin >> t;
while (t--) {
ll n;
ll ans = 0;
cin >> n;
for (int i = 0; i < n; i++)cin >> a[i];
for (int i = 0; i < n; i++) {
if (a[i] == 0) {
dp1[0] += dp1[0] + 1;
dp2[2] += dp2[2];
}
else if (a[i] == 1) {
dp1[1] += dp1[1] + dp1[0];
dp2[1] += dp2[1] + 1;
dp2[3] += dp2[3];
}
else {
dp1[a[i]] += dp1[a[i]] + dp1[a[i] - 1];
dp2[a[i]] += dp1[a[i] - 2] + dp2[a[i]];
dp2[a[i] + 2] += dp2[a[i]+2];
}
dp1[a[i]] = dp1[a[i]] % mdl;
dp2[a[i]] = dp2[a[i]] % mdl;
dp2[a[i] + 2] = dp2[a[i] + 2] % mdl;
}
for (int i = 0; i <= n; i++) {
ans += dp1[i] + dp2[i];
ans = ans % mdl;
}
cout << ans << endl;
clr(dp1, n + 1);
clr(dp2, n + 1);
}


}
/*
2
1 3 1 2 3
2 6 -1 4 -2 3 -2 3
*/