zoukankan      html  css  js  c++  java
  • Codeforces Round #641 (Div. 1)

    https://codeforces.com/contest/1349

    记错时间错过了。但是感觉可以补一下。一天一场div1(1/30)。

    A - Orac and LCM

    题意:给 (nin[1,100000]) 个数的数组 (a)(a_i) 范围在 ([1,200000]) 。定义一个集合 (s)(gcd) 为被 (s) 中的所有数整除的最大的数,定义一个集合 (s)(lcm) 为整除 (s) 中的所有数的最小的数。取出数组 (a) 的所有两两组合数数对的 (lcm) 记作集合 (t) ,求 (gcd(t))

    题解:就是说先取数对的 (lcm) 然后再取所有数的 (gcd) ,首先因为 (lcm) 每种质因子中最大的那个一定会出现在 (t) 的某个数的因子中,但是这好像没有用因为会被 (gcd) 去掉。

    所以直接就是求所有数的 (gcd) ?观察第二个样例 (lcm(10,24)=120) ,确实包含因子 (40) ,答案并不是 (2) 。仔细一想这种题肯定是直接分解成质因数形式观察就可以了。

    10 24 40 80
    

    以质因子 (2) 的角度考察,得到

    1 2 3 4
    

    他们的 (lcm) 集合为

    2 3 4 3 4 4
    

    gcd的结果为

    2
    

    所以就是取这种质因数中最小的两个就可以了,考虑如何快速对这些数进行质因数分解,最简单的思路就是直接random_shuffle或者sort,然后验证所有的质因子 (k) ,若是某个质因子 (k) 有至少2个数不含 (k) 则直接break。注意到每个 (a_i) 至多有 (6) 种不同的质因子,应该大部分都是提前break的了,假如有某个质因子到很后才break,则这种质因子不应该超过十几种。

    PS:溢出了,太久不打搞得离谱,还好没打呢,好演啊。

    int n;
    int a[100005];
    bool vis[200005];
    
    int check(int d) {
        int mind1 = INF, mind2 = INF;
        int cnt0 = 0;
        for(int i = 1; i <= n; ++i) {
            if(a[i] % d != 0) {
                ++cnt0;
                if(cnt0 >= 2)
                    return 1;
            }
            int cnt = 0;
            int x = a[i];
            while(x % d == 0) {
                ++cnt;
                x /= d;
            }
            if(cnt < mind2) {
                mind2 = cnt;
                if(mind2 < mind1)
                    swap(mind1, mind2);
            }
        }
        int res = 1;
        while(mind2--)
            res *= d;
        return res;
    }
    
    void TestCase() {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);
        sort(a + 1, a + 1 + n);
        ll ans = 1;
        for(int i = 2; i <= 200000; ++i) {
            if(vis[i])
                continue;
            ans *= check(i);
            for(int j = 2 * i; j <= 200000; j += i)
                vis[j] = 1;
        }
        printf("%lld
    ", ans);
        return;
    }
    

    另一种解法是,求出前缀gcd和后缀gcd,然后再全部搞一次lcm,这个看起来就太复杂了。

    B - Orac and Medians

    题意:给一个 (nin[1,100000]) 个数的数组 (a) ,每次操作可以选择一段连续区间,然后把这段区间的值全部替换成这段区间的中位数,假如区间的长度是偶数则选择小的那个(左)中位数。问是否能把数组 (a) 变为全部都是 (k)

    (假的)题解:首先 (k) 必须要出现。然后,发现一个惊人的事实: (k) 可以“感染”两侧的比它大的数!而连续两个的 (k) 可以“感染”两侧的比它小的数!所以只要存在一个 (k) 的两侧至少有一个大于等于它的数,就是yes。

    但这只是充分条件而不是必要条件,经过这个检测之后还暂时是no的话,则说明所有的 (k) 两侧都是比它小的数,但是可能可以通过选取一个区间使得 (k) 恰好成为(左)中位数。基于上面的分析,我们得知只要有一个长度至少为2的区间使得 (k) 为(左)中位数,答案就是yes。

    这个可能要借助一下数据结构?把所有比 (k) 小的视作 (-1) ,把所有比 (k) 大的视作 (+1) ,好像变成了最大子数组和dp?

    参考一下别人的:https://www.cnblogs.com/dysyn1314/p/12881386.html

    再看一下官方题解,貌似是只要存在一对相邻的大于等于 (k) 的数,他们之间夹的小于 (k) 的数小于等于1,就可以构造。充分性是显然的:首先,选择这一对数作为左右边界,那么区间内的数都会变成大于等于 (k) ,然后选择其中的连续的两个大于等于 (k) ,再加上新的一个数,一定可以变成三个大于等于 (k) (也就是说可以向两侧扩展),若这两个数之中至少有一个是 (k) ,那么这就是一个构造,否则向两侧扩展之后必定会遇到 (k) 然后被 (k) 感染。

    必要性的话就不是特别显然,若任意两个相邻的大于等于 (k) 的数之间夹了至少两个小于 (k) 的数,则无论怎么选择都只能把区间变成小于 (k) 的数。

    这一场也太恶心了吧。

    int n, k;
    
    void TestCase() {
        scanf("%d%d", &n, &k);
        int prev_ge = -1, mindis = INF, exist_k = 0;
        for(int i = 1, a; i <= n; ++i) {
            scanf("%d", &a);
            if(a >= k) {
                if(prev_ge != -1)
                    mindis = min(mindis, i - prev_ge);
                prev_ge = i;
                if(a == k)
                    exist_k = 1;
            }
        }
        if(exist_k == 1 && (n == 1 || mindis <= 2))
            puts("yes");
        else
            puts("no");
        return;
    }
    
  • 相关阅读:
    抓取csdn上的各类别的文章 (制作csdn app 二)
    Android 使用Fragment,ViewPagerIndicator 制作csdn app主要框架
    MongoDB基本使用
    MongoDB之DBref(关联插入,查询,删除) 实例深入
    nginx 1.4.7 发送日志到rsyslog
    nginx 编译参数
    nginx 编译参数
    rsyslog 传输日志
    rsyslog 传输日志
    rsyslog 直接读取日志,当日志截断后,不会继续发送
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/12879738.html
Copyright © 2011-2022 走看看