「AGC036B」Do Not Duplicate

Description

给一个长度为 $n$ 的序列 $a_i$ 和一个数 $k$ 。现在把 $a$ 序列重复 $k$ 次生成一个数列 $b$ ,然后从第一位开始往一个栈中加入 $b_i$ 。如果这个栈里存在元素与 $b_i$ 相等那么一直 $\text{pop}$ 直到把那个数弹掉然后不加入新数;否则 $\text{push}$ $b_i$ 入栈。求全部操作完之后栈中的元素。

数据范围:$n \leq 2 \times 10^5, k \leq 10^{12}, a_i \leq 2 \times 10^5$

Solution

把 $a_i$ 排在一个圆周上,定义 $nxt_i = j$ 表示沿着顺时针方向第一个 $j$ 使得 $a_i=a_j$ 。

可以发现,假设目前栈中第一个元素是 $a_x = y$ ,那么下一次这个栈被整体清空是加入了
$nxt_x$ 之后。特别的,如果 $nxt_x = x$ ,那么就是再加一遍整个 $a_i$ 之后再次清空。

自然地可以想象 $x$ 向 $nxt_x +1$ 加一条边,表示 $x$ 之后下一个第一个下标会是 $nxt_x + 1$。这样的得到的图会形成一个一个环。也就是说,加了若干次 $a$ 后,全部都被清空,回到原点,也就是循环节。

我们可以把 $k$ 模上循环节,然后再对余下的不完整的部分直接模拟跳环的过程。如果一个时刻所加的数总数超出了 $k$ 就直接能够模拟一遍得到答案。

由于最后跳环每次最多会跳出去 $n+1$ 个点,并且循环节长度不会超过 $n(n+1)$ ,所以最后最多跳 $n$ 次以及统计答案时最多只需要考虑 $n$ 个点。所以时间复杂度为 $O(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
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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
/**
* Author: AcFunction
* Date: 2019-09-04 21:48:14
* Email: 3486942970@qq.com
**/

#include <bits/stdc++.h>

using namespace std;

#define ll long long
#define ull unsigned long long
#define db double
#define ldb long double

#define fi first
#define se second
#define MP make_pair
#define pii pair <int, int>
#define pil pair <int, ll>
#define pli pair <ll, int>
#define pll pair <ll, ll>

#define All(x) x.begin(), x.end()
#define pb push_back
#define pf push_front

#define ms0(x) memset(x, 0, sizeof(x))
#define ms1(x) memset(x, -1, sizeof(x))

#define oiu cerr << "Yes!" << endl;
#define O(x) cerr << #x << ": " << x << endl;

template <typename T> void printarr(T a[], int b, int e) {
if(b > e) return ;
for(int i = b; i < e; i++) cout << a[i] << " ";
cout << a[e] << " " << endl;
}

template <typename T> int chkmax(T &x, const T &y) {
return x < y ? x = y, 1 : 0;
}

template <typename T> int chkmin(T &x, const T &y) {
return x > y ? x = y, 1 : 0;
}

const int N = 5001000;

int n;
int nxt[N], fir[N], pos[N], a[N], ans[N], cnt, len[N];
ll k;

int main() {
scanf("%d %lld", &n, &k); k *= n;
for(int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
if(!fir[a[i]]) {
fir[a[i]] = i;
pos[a[i]] = i;
}
else {
nxt[pos[a[i]]] = i;
pos[a[i]] = i;
}
nxt[i] = fir[a[i]];
}
for(int i = 1; i <= n; i++) {
if(nxt[i] > i) {
len[i] = nxt[i] - i + 1;
}
else len[i] = n - (i - nxt[i] - 1);
}
int u = 1; ll num = 0;
while(1) {
num += len[u];
u = nxt[u] + 1;
if(u > n) u -= n;
if(u == 1) break ;
}
k = k % num;
if(!k) return 0;
u = 1; num = 0;
// k *= n;
while(1) {
num += len[u];
if(num >= k) {
num -= len[u];
for(int j = u; j <= u + k - num - 1; j++) {
int t = j; if(t > n) t -= n;
ans[++cnt] = a[t];
}
for(int i = 1; i <= cnt; i++)
pos[ans[i]] = fir[ans[i]] = nxt[i] = 0;
for(int i = 1; i <= cnt; i++) {
if(!fir[ans[i]]) {
fir[ans[i]] = i;
pos[ans[i]] = i;
}
else {
nxt[pos[ans[i]]] = i;
pos[ans[i]] = i;
}
nxt[i] = fir[ans[i]];
}
int p = 1;
while(p <= cnt) {
if(nxt[p] > p) {
p = nxt[p] + 1;
}
else {
printf("%d ", ans[p]);
p++;
}
}
return 0;
}
u = nxt[u] + 1;
if(u > n) u -= n;
}
return 0;
}