CF1834E MEX of LCM

也许更好的阅读体验

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 n3105 a i ≤ 1 0 9 a_i\le 10^9 ai109

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;
}

如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
校园失物招领系统管理系统按照操作主体分为管理员和用户。管理员的功能包括字典管理、论坛管理、公告信息管理、失物招领管理、失物认领管理、寻物启示管理、寻物认领管理、用户管理、管理员管理。用户的功能等。该系统采用了Mysql数据库,Java语言,Spring Boot框架等技术进行编程实现。 校园失物招领系统管理系统可以提高校园失物招领系统信息管理问题的解决效率,优化校园失物招领系统信息处理流程,保证校园失物招领系统信息数据的安全,它是一个非常可靠,非常安全的应用程序。 ,管理员权限操作的功能包括管理公告,管理校园失物招领系统信息,包括失物招领管理,培训管理,寻物启事管理,薪资管理等,可以管理公告。 失物招领管理界面,管理员在失物招领管理界面中可以对界面中显示,可以对失物招领信息的失物招领状态进行查看,可以添加新的失物招领信息等。寻物启事管理界面,管理员在寻物启事管理界面中查看寻物启事种类信息,寻物启事描述信息,新增寻物启事信息等。公告管理界面,管理员在公告管理界面中新增公告,可以删除公告。公告类型管理界面,管理员在公告类型管理界面查看公告的工作状态,可以对公告的数据进行导出,可以添加新公告的信息,可以编辑公告信息,删除公告信息。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值