Codeforces Round #626 (Div. 2, based on Moscow Open Olympiad in Informatics)

Posted bozhao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Round #626 (Div. 2, based on Moscow Open Olympiad in Informatics)相关的知识,希望对你有一定的参考价值。

Codeforces Round #626 (Div. 2, based on Moscow Open Olympiad in Informatics)

题目 难度 思路
A 简单 贪心
B 一般 思维+数学
C 简单 贪心
D 较难 数学+二进制+二分

A. Even Subset Sum Problem

题意:

找出一个子序列使得子序列的和为偶数

题解:

A:直接贪心, 有偶数的选一个, 没用偶数的选两个(奇数加奇数 = 偶数)若没用两个奇数输出-1.

B: 是的, 没毛病!

代码:

    t = int(input())
     
    for i in range(0, t):
        n = int(input())
        
        a = input().split(" ")
        ans = 0
        for it in range(0, n):
            if(int(a[it], 10) % 2 == 0):
               ans = it + 1
               break;
        if(ans):
            print("1")
            print(ans)
        else:
            if n > 1:
                print("2")
                print("1 2")
            else:
                print("-1")

B. Count Subrectangles

题意:

给你一个 长度为n 的 a数组, 和一个长度为m的b数组, 用a数组与b数组构造一个矩阵c, 其中

c(i, j) = a[i] * a[j],然后问矩阵c中有多少个子矩阵里面的元素和为k, 且子矩阵中不能含有0.

题解:

A:这题你咋想的?

B: 将k的所有因子都找出来, 然后枚举因子。

A:能具体点吗?

B:因为子矩阵的元素和为(k) , 有因为子矩阵的元素和等于(x * y) (其中 (x) 为 a中连续的(1)个数, (y) 为b中连续1的个数), 所有要使得 (x* y = k, x和y) 是k的因子。

A: 那如何枚举呢?

B: 当我们 枚举的因子为 (cnt) 先在a中找到 连续的1长度为cnt的子串个数, 然后再从b中找连续的1长度为$ frac{k}{cnt}$

的子串个数, 然和两个个数相乘就是当前因子产生的贡献, 枚举所有因子就是答案了。

A:哦哦,我懂了。

    #include<bits/stdc++.h>
    using namespace std;
    int n, m, k;
    typedef long long ll;
    const int N = 1e5 + 7;
    int a[N], b[N];
    vector<int>v;
     
    int main(){
        scanf("%d %d %d", &n, &m, &k);
        for(int i = 1; i <= n; i++){
            scanf("%d", &a[i]);
        }
        for(int i = 1; i <= m; i++){
            scanf("%d", &b[i]);
        }
        for(ll i = 1; i * i <= k; i++){
            if(k % i == 0){
                v.push_back(i);
                if(k / i != i){
                    v.push_back(k / i);
                }
            }
        }
        ll ans = 0;
        for(int i = 0; i < v.size(); i++){
            int cnt = 0, x = 0, y = 0;
            for(int j = 1; j <= n; j++){
                if(a[j] == 1){
                    cnt++;
                }else{
                    cnt = 0;
                }
                if(cnt == v[i])x++, cnt--;
            }
            cnt = 0;
            for(int j = 1; j <= m; j++){
                if(b[j] == 1){
                    cnt++;
                }else{
                    cnt = 0;
                }
                if(cnt == (k / v[i]))y++, cnt--;
            }
            ans += 1ll * x * y;
        }
        printf("%lld
", ans);
    }

C. Unusual Competitions

题意:

给你一个括号序列, 然后你可以选择一个子串进行任意次交换使得这个子串的括号序列合法, 然后对答案的贡献就是你选择的子串长度, 问使整个序列都合法, 所得到的答案最小是多少?

题解:

A: 这题贪心就行了。

B: 如何贪心呢?

A: 题目说选择一个子串可以进行任何次操作,使子串合法就行, 所有直接选一个最长且不合法且左括号和有括号数量相等的子串。

B:厉害呀,我怎么没想到。

    #include<bits/stdc++.h>
    using namespace std;
     
    int n;
    string s;
     
     
     
    int main(){
        scanf("%d", &n);
        cin >> s;
        if(n % 2){
            puts("-1");
        }else{
            int ans = 0;
            for(int i = 0; i < n; i++){
                if(s[i] == ‘(‘)ans++;
                else ans--;
            }
            if(ans != 0){
                puts("-1");
            }else{
                int l = 0, r = 0;
                for(int i = 0; i < n; i++){
                    if(s[i] == ‘(‘) l++;
                    else if(l && r == 0) l--;
                    else r++;
     
                    if(r == l){
                        ans += (l + r);
                        l = r = 0;
                    }
     
                }
                printf("%d
", ans);
            }
        }
    }

D. Present

题意:

给你个长度为n的数组a, 让你求:(a1+a2)⊕(a1+a3)⊕…⊕(a1+an)⊕(a2+a3)⊕…⊕(a2+an)…⊕(an?1+an)

这个的答案。

题解:

A:这题好难呀!我完全没用思路。

B:嗯嗯, 这题确实比较难, 我们可以反向操作。

A: 反向操作?

B: 是的, 我们可以先假设这个答案为(x) 如果 (x) 的二进制第 (i)(1) , 说明一定有奇数个((a[i] + a[j]) ) 的二进制第 (i) 位是1。

A: 没错, 因为是异或吗奇数个1异或肯定是1, 偶数个1异或肯定是0。那如何判断有多少个 ((a[i] + a[j]) )的二进制第 (i) 位是1呢?

B:这个……, 我们可以先把所有的直接模上 (2 ^{i + 1}) , 因为 (2 ^{i + 1}) 的第 (i) 是否位1, 没用任何影响。 然后从小到大排序,如果 (a[i] + a[j]) 的第 (i) 是1, 那么(a[i] + a[j]) 的范围一定在 $[2^{i}, 2 ^{i + 1} - 1] $ 或者 ([2^{i + 1} + 2 ^ {i}, 2^{i + 2} - 2]) 之间, 那么你枚举一个数, 另一个数用二分去查有多少个就行了。

A:哦哦, 我懂了,a[1]查到有 a[2]可以组合, 然后a[2]又查到了与a[1]可以组合,这时就会用重复计算。

B:嗯嗯, 这个你之间对总的贡献除2就好了。

#include<bits/stdc++.h>
using namespace std;

const int N = 4e5 + 7;
typedef long long ll;
int n;
ll a[N], fn[N];

vector<ll> v;

int main(){
    scanf("%d", &n);
    for(int i = 1; i <= n; i++){
        scanf("%lld", &a[i]);
    }
    fn[0] = 1;
    for(int i = 1; i <= 32; i++){
        fn[i] = fn[i - 1] * 2;
    }
    ll ans = 0;
    for(int i = 0; i < 30; i++){
        ll mod = fn[i + 1];
        ll cnt = 0;
        v.clear();
        for(int i = 1; i <= n; i++){
            v.push_back(a[i] % mod);
        }
        sort(v.begin(), v.end());
        for(int j = 0; j < v.size(); j++){
            ll l = max(1ll*0, fn[i] - v[j]);
            ll r = fn[i + 1] - 1 - v[j];
            cnt += upper_bound(v.begin(), v.end(), r) - lower_bound(v.begin(), v.end(), l);
            l = max(1ll * 0, fn[i + 1] + fn[i] - v[j]);
            r = fn[i + 2] - 2 - v[j];
            cnt += upper_bound(v.begin(), v.end(), r) - lower_bound(v.begin(), v.end(), l);
            if(((2 * v[j]) & fn[i])){
                cnt--; 	// 去除自己本身
            }

        } 
        if((cnt / 2) % 2){
            ans += fn[i];
        }
      
    }
    printf("%lld
", ans);

}

以上是关于Codeforces Round #626 (Div. 2, based on Moscow Open Olympiad in Informatics)的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Round #626 (Div. 2) B. Count Subrectangles

Codeforces Round #626 (Div. 2, based on Moscow Open Olympiad in Informatics)

Codeforces Round #626 D. Present 异或按位确定 +二分or双指针

Codeforces Round #626 (Div. 2, based on Moscow Open Olympiad in Informatics)

Codeforces Round #626 (Div. 2, based on Moscow Open Olympiad in Informatics)ABCD(题解)

Codeforces Round #626 (Div. 2, based on Moscow Open Olympiad in Informatics)(A-C)