D
e
s
c
r
i
p
t
i
o
n
\mathcal{Description}
Description
给
n
n
n个数,将这
n
n
n个数所有子区间的
l
c
m
lcm
lcm作为一个集合
S
S
S,求最小的没有出现在
S
S
S中的数
(
m
e
x
{
S
}
)
(mex\{S\})
(mex{S})
e
g
:
eg:
eg:有
3
3
3个数
1
2
3
1\ 2\ 3
1 2 3,所有子区间的
l
c
m
lcm
lcm构成集合
{
1
2
3
6
}
\{1\ 2\ 3\ 6\}
{1 2 3 6},第一个未出现的数是
4
4
4
n
≤
3
∗
1
0
5
n\le 3*10^5
n≤3∗105,
a
i
≤
1
0
9
a_i\le 10^9
ai≤109
S
o
l
u
t
i
o
n
\mathcal{Solution}
Solution
考虑已知一个区间的
l
c
m
lcm
lcm,此时将区间往两边扩大
1
1
1,
l
c
m
lcm
lcm是不降的
在知道上面之后,我们想要知道所有区间的 l c m lcm lcm中最小的未出现过数,因此我们不需要一开始就知道所有的 l c m lcm lcm,考虑按从小到大构造出 l c m lcm lcm,又知道区间 l c m lcm lcm在区间变大的过程中是一直增大的
立马就能想到用一个小根堆,最开始将每个位置上的数存进去表示以这个位置作为区间 l c m lcm lcm的起点,用 a n s ans ans表示当前考虑 a n s ans ans是否是答案,每次取出最小的数出来看是否等于 a n s ans ans,如果等于 a n s ans ans那么 a n s ans ans就不能作为答案, a n s ans ans需要增大,然后将堆顶的元素取出来让它的区间往右走一步再重新插进堆中,这样就能保证当第一次堆顶元素不等于 a n s ans ans时就找到了答案
然而这样会超时,因为会有大量的重复的计算,比如 1 , 1 , 1 , 1 , 1 , 1 ⋯ 1,1,1,1,1,1\cdots 1,1,1,1,1,1⋯这样的序列,仅仅只是想让 a n s ans ans变成2,就需要 n 2 n^2 n2复杂度不断扩大区间
考虑什么样的情况是不需要重复计算的,当有若干个区间右端点相同,左端点不同的区间的 l c m lcm lcm相同时,只需要保留一个区间即可,例如, [ 1 , 4 ] [1,4] [1,4]的 l c m lcm lcm等于 [ 3 , 4 ] [3,4] [3,4]的 l c m lcm lcm时, [ 1 , 4 ] [1,4] [1,4]便不需要继续往右走了,因为答案会完全和 [ 3 , 4 ] [3,4] [3,4]相同
因此考虑记忆每个位置曾经出现过哪些值,当某个区间的右端点扩大后发现这个位置曾经有过区间的 l c m lcm lcm和自己相同,那么就不用将这个区间加入堆中了,用 s e t set set记忆这些值即可
C o d e \mathcal{Code} Code
#include <cstdio>
#include <queue>
#include <set>
#define ll long long
#define mp make_pair
using namespace std;
const int maxn = 3e5 + 5;
int T, n;
int x[maxn];
set <ll> now[maxn];
priority_queue < pair<ll, int> > q;
ll gcd (ll a, ll b)
{
if (!b) return a;
return gcd(b, a % b);
}
ll lcm (ll a, ll b){ return a * b / gcd(a, b); }
int main ()
{
scanf("%d", &T);
while (T--) {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &x[i]), q.push(mp(-x[i], i)), now[i].clear(), now[i].insert(x[i]);
ll ans = 1;
while (!q.empty() && q.top().first == -ans) {
while (!q.empty() && q.top().first == -ans) {
ll l = -q.top().first;
int i = q.top().second;
q.pop();
if (++i <= n) {
l = lcm(l, x[i]);
if (now[i].find(l) == now[i].end()) q.push(mp(-l, i)), now[i].insert(l);
}
}
++ans;
}
while (!q.empty()) q.pop();
printf("%lld\n", ans);
}
return 0;
}
如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧