题目大意
有一个长度为 n 的数组 a0,⋯,an−1,把它拼 k 次,得到 a0,⋯,ank−1。
有一个初始为空的数组 X,对于 0≤i<nk,依次进行下面的操作:
- 若 X 不含 ai,则把 ai 加到 X 的末尾;
- 若 X 含有 ai,则一直删除 X 的末尾直至 X 不含 ai。
求最终的 X 数组。
n,ai≤2×105, k≤1012
题解
把 aik,⋯,aik+n−1 叫做第 i 层。
假设 a0,⋯,an−1 是个循环数组,每个 ai 找到它下一个跟他相同的元素,假设是 aj,那么 i 向 (j+1)modn 连边,边权为这条边跨过了几层。比如:

由于每个点都有一条出边,那么这回是一棵环套树,也即有循环节。
O(n) 找到循环节,然后通过循环节快速走到最后一层,再暴力最后一层。
题解没有找循环节,而是用倍增来求每个点走 2k 层之后到哪。萌新第一次艹标算好紧张啊。。
代码
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
| #include<bits/stdc++.h> #define fo(i,a,b) for(int i=a;i<=b;i++) #define fd(i,a,b) for(int i=a;i>=b;i--) using namespace std;
typedef long long LL;
struct D{ int i; LL k; };
const int maxn=2e5+5;
int n,a[maxn],occur[maxn],d0; LL k; D p[maxn],d[maxn]; bool bz[maxn];
int ans0,ans[maxn]; void Last(int x) { memset(bz,0,sizeof(bz)); fo(i,x,n) if (!bz[a[i]]) { ans[++ans0]=a[i]; bz[a[i]]=1; } else { while (bz[a[i]]) bz[ans[ans0--]]=0; } fo(i,1,ans0) printf("%d ",ans[i]); }
int main() { scanf("%d %lld",&n,&k); fo(i,1,n) scanf("%d",&a[i]); fd(i,n,1) { if (occur[a[i]]) { if (occur[a[i]]==n) p[i]=(D){1,1ll}; else p[i]=(D){occur[a[i]]+1,0ll}; } occur[a[i]]=i; } memset(occur,0,sizeof(occur)); fo(i,1,n) { if (!occur[a[i]]) occur[a[i]]=i; if (!p[i].i) { if (occur[a[i]]==n) p[i]=(D){1,2ll}; else p[i]=(D){occur[a[i]]+1,1ll}; } } D i=(D){1,1}; memset(occur,0,sizeof(occur)); for(; i.k<k; i.k+=p[i.i].k, i.i=p[i.i].i) { d[++d0]=i; if (occur[i.i]) break; else occur[i.i]=d0; } if (i.k==k) {Last(i.i); return 0;} LL cir=d[d0].k-d[occur[d[d0].i]].k; i.k=d[occur[d[d0].i]].k+(k-d[occur[d[d0].i]].k)/cir*cir; for(; i.k<k; i.k+=p[i.i].k, i.i=p[i.i].i); Last(i.i); }
|