题意:
给定一个长度为
n
n
n的序列
a
a
a,让你找出一对
k
k
k和
p
p
p使得对于
i
>
k
i>k
i>k,
都有
a
[
i
]
=
a
[
i
+
p
]
a[i]=a[i+p]
a[i]=a[i+p],要求使得
k
+
p
k+p
k+p最小,当多个
k
+
p
k+p
k+p相同时,
p
p
p尽可能小。
数据范围:
1
≤
n
≤
1
0
6
1\leq n\leq 10^6
1≤n≤106
题解:
先将序列翻转过来得到
a
r
ar
ar,那么循环节元素必然是从
a
r
[
1
]
ar[1]
ar[1]到
a
r
[
p
]
ar[p]
ar[p]。
所以我们需要知道如何快速判断,考虑到循环节的特性,求解
n
e
x
t
next
next数组。
考虑枚举每个元素作为其所在循环节的最后一个元素,
完整的循环节长度
p
=
i
−
n
e
x
t
[
i
]
p=i-next[i]
p=i−next[i],非循环节部分长度为
k
=
n
−
i
k=n-i
k=n−i。
这部分完整循环节为:
a
r
[
i
−
n
e
x
t
[
i
]
+
1
,
i
]
ar[i-next[i]+1,i]
ar[i−next[i]+1,i]
理解的方式:序列为未翻转的状态,那么可以知道
a
[
k
+
1
,
k
+
i
−
n
e
x
t
[
i
]
]
a[k+1,k+i-next[i]]
a[k+1,k+i−next[i]]就是翻转的
a
r
[
i
−
n
e
x
t
[
i
]
+
1
,
i
]
ar[i-next[i]+1,i]
ar[i−next[i]+1,i]。
因此这样我们只需要对每次枚举得到的 p , k p,k p,k对最终答案更新即可。由于 k k k枚举时递减,所以当 p + k p+k p+k相同时,越往后枚举得到的 p p p必然越大,因此只有 p + k p+k p+k小于当前答案才能更新。
代码:
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 10;
int a[N], n;
int ne[N];
void get_next() {
for(int i = 2, j = 0; i <= n; ++i) {
while(j && a[i] != a[j + 1]) j = ne[j];
if(a[i] == a[j + 1]) ++j;
ne[i] = j;
}
}
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; ++i) scanf("%d", &a[i]);
reverse(a + 1, a + n + 1);
get_next();
int p = 1, k = n - 1;
for(int i = 2; i <= n; ++i) {
int tp = i - ne[i];
int tk = n - i;
if(tp + tk < p + k) p = tp, k = tk;
}
printf("%d %d\n", k, p);
return 0;
}