很巧妙的一道题
首先我们考虑计算nxt数组,表示从第
i
i
i个位置起第一个下标
r
r
r使得
a
l
.
.
r
a_{l..r}
al..r为可灭绝的,只要求出nxt[i]我们就可以使用DP在
O
(
N
)
O(N)
O(N)时间内求出答案。
考虑计算nxt[i]数组的方法,如果直接计算总复杂度是
O
(
N
2
)
O(N^2)
O(N2)的,我们要寻找在
O
(
N
)
O(N)
O(N)时间内求出这个数组的方法。
添加一个map nxtX[i][j],表示第一个下标
r
+
1
r + 1
r+1使得
a
l
.
.
.
r
a_{l...r}
al...r为可灭绝的并且
a
r
+
1
=
j
a_{r+1}=j
ar+1=j,由nxtX[i]我们可以轻易求出nxt[i],
n
x
t
[
i
]
=
n
x
t
X
[
i
+
1
]
[
a
[
i
]
]
nxt[i]=nxtX[i+1][a[i]]
nxt[i]=nxtX[i+1][a[i]],nxtX怎么求呢?
假设我们已经更新完nxt[i],我们可以让
n
x
t
X
[
i
]
=
n
x
t
X
[
n
x
t
[
i
]
+
1
]
nxtX[i]=nxtX[nxt[i]+1]
nxtX[i]=nxtX[nxt[i]+1],然后让
n
x
t
X
[
i
]
[
a
n
x
t
[
i
]
+
1
]
=
n
x
t
[
i
]
+
1
nxtX[i][a_{nxt[i]+1}]=nxt[i]+1
nxtX[i][anxt[i]+1]=nxt[i]+1。每次循环结束后把自己更新一下
n
x
t
[
i
]
[
a
[
i
]
]
=
i
nxt[i][a[i]]=i
nxt[i][a[i]]=i。
需要注意的是,对于这个map数组,如果直接赋值的话是可以卡到
O
(
N
2
)
O(N^2)
O(N2)的,所以需要swap。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<map>
using namespace std;
const int N = 300010;
int nxt[N], a[N], dp[N];
map<int, int> nxtX[N];
int main()
{
int n, T;
cin>>T;
while(T--){
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
for (int i = 1; i < n + 3; ++i){
nxt[i] = -1;
nxtX[i].clear();
dp[i] = 0;
}
for (int i = n; i; --i){
if (nxtX[i + 1].count(a[i])){
int pos = nxtX[i + 1][a[i]];
if (pos <= n && a[pos] == a[i]){
nxt[i] = pos;
swap(nxtX[i], nxtX[nxt[i] + 1]);
if (pos < n) nxtX[i][a[pos + 1]] = pos + 1;
}
}
nxtX[i][a[i]] = i;
}
long long ans = 0;
for (int i = n; i; i--){
if (nxt[i] == -1) continue;
dp[i] = dp[nxt[i] + 1] + 1;
ans += dp[i];
}
printf("%lld\n", ans);
}
}