抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

原题链接

有一个长度为 n(n105)n(n\le 10^5) 的序列{a}(ai105)\{a\}(a_i\le 10^5),你可以对它进行操作,操作如下:首先选择三个数v,t,kv, t, k,满足av=av+t=av+2t==av+kta_v = a_{v+t} = a_{v + 2t} = \cdots = a_{v + kt},然后将其删除,得到一个新的序列。每次操作结束后你都能将新数列重排。

现有 Q(Q105)Q(Q\le 10^5) 个询问,每次询问 [l,r][l, r] 表示问要把 [l,r][l, r] 删除所需的最小步数。

(以上翻译改写自 洛谷

题解

感觉像是 HH 的项链 的升级版。

不难发现答案就是 [l,r][l, r] 不同数的个数 ++ 第一次在 [l,r][l,r] 中是否有一种数被删掉。

记两个数组 fa[i]fa[i]ffa[i]ffa[i]fa[i]fa[i]表示上一次第 ii 个数出现在了那里,ffa[i]ffa[i]表示如果询问的 lffa[i]l\le ffa[i],那么在[l,r][l, r] 区间内与第 ii 个数相同的不可能被一次性删除。

大致就是这样一幅图:

黑色表示 fa[i]fa[i],红色表示ffa[i]ffa[i](如果fa[i]fa[i]ffa[i]ffa[i]连到了 00 就没画)。

不难得出如果ifa[i]=fa[i]fa[fa[i]]i - fa[i] = fa[i] - fa[fa[i]],那么ffa[i]=ffa[fa[i]]ffa[i] = ffa[fa[i]],否则ffa[i]=fa[fa[i]]ffa[i] = fa[fa[i]]

1
2
3
4
5
6
7
for(int i = 1, aa; i <= n; ++i)
{
scanf("%d", &aa);
fa[i] = lst[aa];
lst[aa] = i;
ffa[i] = (fa[i] - fa[fa[i]] == i - fa[i]) ? ffa[fa[i]] : fa[fa[i]];
}

我们考虑离线,让询问按 rr 为关键字排序。

考虑查询 [l,r][l, r],如果是这样一段序列:1,2,2,2,31, 2, 2, 2, 3,我们发现22 出现了 33 次,我们钦定最后一个 22 对答案有贡献。这样我们对所有相同的数,都钦定最后一个对答案有贡献,其余对答案即可。这样没加入一个数,就在线段树 / 树状数组对 ii 位置加 11,在fa[i]fa[i] 位置减 11 即可(把前一个数的贡献删去)。

对于询问第一次能否完全删除一个数,我们只需查询有多少个数在 [l,r][l, r] 上不是均匀分布即可。同理,我们钦定最后一个对答案有贡献,没加入一个 ii 时,在 ffa[fa[i]]ffa[fa[i]]1-1,在 ffa[i]ffa[i]+1+1即可。

代码

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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <cstdio>
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn = 100005;

int n;

struct Tree
{
int no[maxn << 2];
int K;

inline void build_tree()
{
for(K = 1; K <= n; K <<= 1);
}

inline void add(int k, int x)
{
if(!k)
return;
for(k += K; k; k >>= 1)
no[k] += x;
}

inline int query(int l, int r)
{
int ans = 0;
for(l += K - 1, r += K + 1; l ^ r ^ 1; l >>= 1, r >>= 1)
{
if(~l & 1)
ans += no[l ^ 1];
if(r & 1)
ans += no[r ^ 1];
}
return ans;
}
} tr1, tr2;

int fa[maxn], ffa[maxn], lst[maxn];

struct ask
{
int l, r, id;

friend bool operator < (ask a, ask b)
{
return a.r < b.r;
}
} q[maxn];

int ans[maxn];

int main()
{
scanf("%d", &n);
tr1.build_tree(), tr2.build_tree();
for(int i = 1, aa; i <= n; ++i)
{
scanf("%d", &aa);
fa[i] = lst[aa];
lst[aa] = i;
ffa[i] = (fa[i] - fa[fa[i]] == i - fa[i]) ? ffa[fa[i]] : fa[fa[i]];
}
int Q;
scanf("%d", &Q);
for(int i = 1; i <= Q; ++i)
{
scanf("%d%d", &q[i].l, &q[i].r);
q[i].id = i;
}
sort(q + 1, q + Q + 1);
for(int i = 1, now = 0; i <= Q; ++i)
{
register int l = q[i].l, r = q[i].r;
while(now < r)
{
now++;
tr1.add(fa[now], -1);
tr1.add(now, 1);
tr2.add(ffa[fa[now]], -1);
tr2.add(ffa[now], 1);
}
int tt1 = tr1.query(l, r), tt2 = tr2.query(l, r);
ans[q[i].id] = tt1 + (tt1 == tt2);
}
for(int i = 1; i <= Q; ++i)
printf("%d\n", ans[i]);
return 0;
}

评论