• Educational Codeforces Round 89 (Rated for Div. 2)


    https://codeforces.com/contest/1366

    A - Shovels and Swords

    贪心

    B - Shuffle

    正解应该是随便乱搞就可以的,不过当时没有仔细想,而且受到以前做过的另一个idea的影响,导致这道题搞复杂了。

    下面的解法可以对付多个 (x) 的情况。

    注意这里只需要记录 (r[i]+1) 进入离散化,和差分的思想类似,每次遇到离散化的点状态才会改变,所以 (r[i]) 不需要记录。易知这里的 (n) 也没什么用。

    int n, m, x;
    
    int idx[2005], top;
    bool suc[2005];
    int l[1005], r[1005];
    
    int id(int k) {
        return lower_bound(idx + 1, idx + 1 + top, k) - idx;
    }
    
    void TestCase() {
        cin >> n >> x >> m;
        top = 0;
        idx[++top] = x;
        idx[++top] = x + 1;
        for(int i = 1; i <= m; ++i) {
            cin >> l[i] >> r[i];
            idx[++top] = l[i];
            idx[++top] = r[i] + 1;
        }
        sort(idx + 1, idx + 1 + top);
        top = unique(idx + 1, idx + 1 + top) - (idx + 1);
    
        fill(&suc[1], &suc[top] + 1, 0);
    
        suc[id(x)] = 1;
        for(int i = 1; i <= m; ++i) {
            int lid = id(l[i]);
            int rid = id(r[i] + 1);
            for(int j = lid; j < rid; ++j) {
                if(suc[j] == 1) {
                    fill(&suc[lid], &suc[rid], 1);
                    break;
                }
            }
        }
        int ans = 0;
        for(int j = 1; j <= top; ++j) {
            if(suc[j])
                ans += idx[j + 1] - idx[j];
        }
        cout << ans << endl;
        return;
    }
    

    fill是真的好用,以后多多注意这个函数的用法。

    C - Palindromic Paths

    注意若有奇数个点,则正中间的“对角线”位置的不用修改。

    *D - Two Divisors

    题意:给 (nin[1,5cdot 10^5]) 个数,数的范围 ([1,10^7]) ,对每个数 (x) 求下面的子问题。

    子问题:给一个数 (x) ,求其两个因数 (d_1>1)(d_2>1) ,满足 (gcd(d_1+d_2,x)=1) ,无解输出两个-1。

    题解:一开始觉得是随便取两个质因数,就可以,但是很显然若输入120就会翻车。一般认为加法和因数并没有什么关系,所以在这里想了很久,但是突然注意到其实输入120可以输出2和15,也可以输出3和10,也可以输出5和6。就猜测是不是所有的质因数,取出其中一个,然后另外的全部乘在一起,再加起来,会得到一个质数呢?这个很明显是不对的,比如2和7加起来就是合数9,但是惊人发现其实加起来的好像和选出的质因数都互质。

    仔细联想一下“不存在最大的质因数”这个证明,就能明白这是为什么。

    显然若 (x) 只有一种质因子,则无解。设 (x)(kgeq2) 个质因子 (p_1,p_2,...,p_k) ,则构造 (d_1=p_1,d_2=p_2p_3...p_k) ,则 (d_1+d_2=p_1+p_2p_3...p_k) ,这个显然不含有 (p_1,p_2,...,p_k) 中的任意一个质因子。(若 (d_1+d_2) 含有质因子 (p_1) 则说明 (p_1|d_2) 但这是不可能的,其他情况同理)

    所以就上网复制一个预处理后 (O(log{x})) 质因数分解的算法。

    int n;
    
    int a[500005];
    
    const int MAXN = 1e7;
    int p[MAXN + 5], ptop;
    int pm[MAXN + 5], pk[MAXN + 5];
    
    void sieve() {
        int n = MAXN;
        pm[1] = 1;
        for(int i = 2; i <= n; ++i) {
            if(!pm[i]) {
                p[++ptop] = i;
                pm[i] = i;
                pk[i] = i;
            }
            for(int j = 1; j <= ptop; ++j) {
                int t = i * p[j];
                if(t > n)
                    break;
                pm[t] = p[j];
                if(i % p[j]) {
                    pk[t] = pk[p[j]];
                } else {
                    pk[t] = pk[i] * p[j];
                    break;
                }
            }
        }
    }
    
    int ans1[500005];
    int ans2[500005];
    
    void TestCase() {
        scanf("%d", &n);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);
        sieve();
        for(int i = 1; i <= n; ++i) {
            if(a[i] == pk[a[i]]) {
                ans1[i] = -1;
                ans2[i] = -1;
            } else {
                int x = a[i];
                ans1[i] = pm[x];
                x /= pk[x];
                ans2[i] = 1;
                while(x != 1) {
                    ans2[i] *= pm[x];
                    x /= pk[x];
                }
            }
        }
        for(int i = 1; i <= n; ++i)
            printf("%d%c", ans1[i], " 
    "[i == n]);
        for(int i = 1; i <= n; ++i)
            printf("%d%c", ans2[i], " 
    "[i == n]);
        return;
    }
    

    仔细想想,其实并不需要 (O(log{x})) 质因数分解。选择两个因数 (d_1=p_1,d_2=x/(p_1)^{alpha_1}) 即可,总体复杂度 (O(n+max{x}))

    其他的想法:

    (gcd(d_1+d_2,x)=1) 显然必要条件为 (gcd(d_1,d_2)=1) ,而这个也是充分条件,因为 (gcd(d_1,d_2)=1)(gcd(d_1+d_2,d_2)=1)(gcd(d_1+d_2,kd_2)=1) ,取 (k=x/d_2) 即可。

    int n;
    
    int p[10000005], ptop;
    int pk[10000005];
    
    void sieve() {
        int n = 10000000;
        for(int i = 2; i <= n; ++i) {
            if(!pk[i]) {
                p[++ptop] = i;
                pk[i] = i;
            }
            for(int j = 1; j <= ptop; ++j) {
                int t = i * p[j];
                if(t > n)
                    break;
                if(i % p[j]) {
                    pk[t] = pk[p[j]];
                } else {
                    pk[t] = pk[i] * p[j];
                    break;
                }
            }
        }
    }
    
    int ans1[500005];
    int ans2[500005];
    
    void TestCase() {
        sieve();
        scanf("%d", &n);
        for(int i = 1, x; i <= n; ++i) {
            scanf("%d", &x);
            if(x == pk[x]) {
                ans1[i] = -1;
                ans2[i] = -1;
            } else {
                ans1[i] = pk[x];
                ans2[i] = x / pk[x];
            }
        }
        for(int i = 1; i <= n; ++i)
            printf("%d%c", ans1[i], " 
    "[i == n]);
        for(int i = 1; i <= n; ++i)
            printf("%d%c", ans2[i], " 
    "[i == n]);
        return;
    }
    

    *E - Two Arrays

    题意:给一个 (n) 个数的数组 (a) 和一个 (m) 个数的数组 (b) 。数组 (b) 单调递增。求把 (a) 划分为 (m) 个子区间 ,使得第 (j) 个子区间的最小值恰好为 (b_j) 的方案数。

    题解:由于 (b) 单调递增,所以先把 (a) 求一次后缀最小值变成非严格单调递增。然后双指针法转移出满足 (a_i=b_j) 的段的长度分别是多少,除了第一段的长度以外,其他的全部乘起来。

    例如用修改一下的第一个样例:

    10 3
    12 10 20 20 25 30 30 35 32 32
    10 20 30
    

    取后缀最小值后为:

    10 3
    10 10 20 20 25 30 30 32 32 32
    10 20 30
    

    那么第一段和第二段的划分位置必须在两个20的前面。
    而且第二段和第三段的划分位置必须在两个30的前面。

    所以答案是4。

    需要注意的是这个25必须分配给第二段,这些32必须分配给第三段。

    为什么要取后缀最小值,目的是把数组 (a) 变成非严格单调递增,因为在这个问题里要匹配一个严格单调递增的数组 (b) ,这样变换是等价的。要保证“第 (j) 个子区间的最小值恰好为 (b_j) ”,则说明这一段必须保留至少一个 (b_j) 并且不能保留任何比 (b_j) 小的数。由于这个限制所以比 (b_j) 大而比 (b_{j+1}) 小的数也必须和 (b_j) 放在一起。

    这个解法可以解决 (b_j) 找不到匹配的 (a_i) 的问题(包括 (m>n) 的情况),因为这种情况下匹配的长度是0。

    但是这个解法少考虑了第一段的 (b_1) 之前的数,若有 (a_1<b_1) 则说明直接无解。

    int n, m;
    int a[200005];
    int b[200005];
    
    void TestCase() {
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; ++i)
            scanf("%d", &a[i]);
        for(int j = 1; j <= m; ++j)
            scanf("%d", &b[j]);
    
        for(int i = n - 1; i >= 1; --i)
            a[i] = min(a[i + 1], a[i]);
    
        ll ans = (a[1] == b[1]);
        for(int j = 1, i = 1; j <= m; ++j) {
            while(i <= n && a[i] < b[j])
                ++i;
            int len = 0;
            while(i <= n && a[i] == b[j]) {
                ++i;
                ++len;
            }
            if(j > 1)
                ans = ans * len % MOD;
        }
        printf("%lld
    ", ans);
        return;
    }
    
  • 相关阅读:
    eclipse maven项目 热部署
    二十三. Django ModelForm组件
    二十二 .Django生命周期
    二十二 .Django form上传+Ajax+FormData上传+Form组件上传+序列化+ifram伪造Ajax
    二十一. Django----ajax全部
    二十. Django ajax--请求数据 和模态对话框
    二十. Django分页 和自定义分页
    十九. Django-Cookie的CBV和FBV的用户验证装饰器
    十八 .Django session应用
    十七 .Django cookie应用
  • 原文地址:https://www.cnblogs.com/KisekiPurin2019/p/13111496.html
Copyright © 2020-2023  润新知