CF1223F Stack Exterminable Arrays

CCF 出的原题观摩一下。

思路

首先可以用一个 Trie 来维护。

在这里对本文中的一些变量做一下说明。

  1. pp 表示当前维护的 Trie 中,指向的元素编号。
  2. tit_i 表示在 Trie 中编号为 ii 的元素在原序列中的值。
  3. fif_i 表示在 Trie 中编号为 ii 的元素在 Trie 中的父节点。
  4. viv_i 表示在 Trie 中编号为 ii 被遍历的次数。

考虑每一次将一个数 aia_i 加入 Trie 的时候需要做什么操作。

如果当前在 Trie 中指向的节点 tpt_paia_i 相等,说明可以进行合并, 那么直接将 pp 跳到 fpf_p 即可;否则需要新开一个节点 vv,接在 pp 的下方,并将 pp 更新到 vv 上。

然后在更新 pp 之后,要将 vpv_p11

考虑如何统计答案。发现点 pp 被遍历过 22 次时,答案会加 1133 次,答案会加 22;以此类推。

这是因为当 vp>1v_p > 1 时,说明 pp 节点已经可以被合并 vp1v_p - 1 次了,所以直接加 vp1v_p - 1 即可。

注意:在代码中为了优美,将 vpv_p 初值设为了 1-1

但是用普通的 Trie 显然是过不了的,因为 Trie 的空间复杂度在本题中会变为 Θ(n2)\Theta(n^2),所以直接开一个 umap 即可。

复杂度为 Θ(nlogn)\Theta(n \log n),实测跑得飞快。

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
#include <bits/stdc++.h>  
#define re register
#define ll long long

using namespace std;

const int N = 3e5 + 10;
int T,n,p,idx;
ll ans;
int arr[N];

struct node{
ll val;
int u,fa;
unordered_map<int,int> son;
}tr[N];

inline int read(){
int r = 0,w = 1;
char c = getchar();
while (c < '0' || c > '9'){
if (c == '-') w = -1;
c = getchar();
}
while (c >= '0' && c <= '9'){
r = (r << 3) + (r << 1) + (c ^ 48);
c = getchar();
}
return r * w;
}

inline void solve(){
ans = 0;
p = idx = 1;
n = read();
for (re int i = 1;i <= n;i++){
tr[i].val = tr[i].u = tr[i].fa = 0;
tr[i].son.clear();
arr[i] = read();
}
for (re int i = 1;i <= n;i++){
if (arr[i] == tr[p].u) p = tr[p].fa;
else{
int v;
if (!tr[p].son.count(arr[i])){
tr[p].son[arr[i]] = v = ++idx;
tr[v] = {-1,arr[i],p};
}
else v = tr[p].son[arr[i]];
p = v;
}
tr[p].val++;
ans += tr[p].val;
}
printf("%lld\n",ans);
}

int main(){
T = read();
while (T--) solve();
return 0;
}

CF1223F Stack Exterminable Arrays
http://watersun.top/[题解]CF1223F Stack Exterminable Arrays/
作者
WaterSun
发布于
2024年6月12日
许可协议