agc036B Do Not Duplicate
题意:给一个长度为$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; }
弱者就是会被欺负呀