标签:bre long 相同 遇到 复杂 printf style 序列 c++
题意:给一个长度为$n$的序列$a_i$和一个数$k$。
现在把$a$序列重复$k$次生成一个数列$b$,然后从第一位开始往一个栈中加入$b_i$ 。
如果这个栈里存在元素与$b_i$相等那么一直pop直到把那个数弹掉然后不加入新数;否则push$b_i$入栈。求全部操作完之后栈中的元素。
$n \leq 2 \times 10^5, k \leq 10^{12}, a_i \leq 2 \times 10^5$
循环节。Yyyyyyyk都知道是循环节。
如果当前栈内第一个元素为$x$,则下一次栈空即为下一次遇到$x$。
所以预处理出每个$a_i$下一个和它相同的位置,假设为$j$,将$i$向$j+1$连边,即下次栈首元素为$a[j+1]$。
可以证明最后连出的一定是若干个环而不是基环内向树,因为每个点有唯一出边且有唯一入边。
也就是说1一定可以走回1,一定存在循环节,环长度最大是n,而对于k来说,循环节长度最大为n+1
所以在环上暴力跑出循环节,将k%循环节长度后,剩下的在环上暴力跑,多出来的一点也暴力跑就好
复杂度$O(n)$
场上想到了循环节,也想到了预处理nxt之类的,但是一直在倒着想,然后就想不出来。
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 7;
long long n, k, d;
int a[maxn], lst[maxn], to[maxn], zz[maxn], t, hs[maxn];
int main() {
scanf("%lld%lld", &n, &k);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
for (int i = n; i; --i) lst[a[i]] = i;
for (int i = n; i; --i) {
to[i] = lst[a[i]];
lst[a[i]] = i;
}
int pos = 1;
do {
if(to[pos] <= pos) ++d;
if(to[pos] == n) ++d;
pos = to[pos] % n + 1;
}while(pos != 1);
k %= d;
if(k == 0) return 0;
pos = 1;
while(1) {
if(to[pos] <= pos) {
if(k > 1) --k;
else break;
}
pos = to[pos] % n + 1;
}
for (int i = pos; i <= n; ++i) {
if(hs[a[i]]) {
//cerr << a[i] << endl;
while(zz[t] != a[i])
hs[zz[t--]] = 0;
hs[zz[t--]] = 0;
}
else {
hs[a[i]] = 1;
zz[++t] = a[i];
}
}
for (int i = 1; i <= t; ++i) printf("%d ", zz[i]);
printf("\n");
return 0;
}
标签:bre long 相同 遇到 复杂 printf style 序列 c++
原文地址:https://www.cnblogs.com/Serene-shixinyi/p/13394587.html