Codeforces Round 919 (Div. 2)(A-D)

题目地址:https://codeforces.com/contest/1920

Problems A. Satisfying Constraints

思路

题目给出若干约束,求符合要求的数字个数。

若为1,则大于等于该约束;若为2,则小于等于该约束;若为3,则不能等于该约束。

对于前两种情况,统计最大左边界和最小右边界;对于第三种情况,存入数组中。然后统计在边界内的第三种情况的个数,最后用边界内数字个数减去区间内第三种情况的个数即可。

标程

void Solved() {
    int n; cin >> n;
    int mx = INF, mi = 0;
    vector<int> a;
    for(int i = 0; i < n; i ++ ) {
        int x, y; cin >> x >> y;
        if(x == 1) {
            mi = max(mi, y);
        } else if(x == 2) {
            mx = min(mx, y);
        } else {
            a.push_back(y);
        }
    }
    int sum = 0;
    for(auto i : a) {
        if(i >= mi && i <= mx) sum ++;
    }
    int res = mx - mi + 1 - sum;
    
    cout << max(res, 0) << endl;
}

Problems B. Summation Game

思路

简单博弈,因为每人只操作一次,Bob希望结果数组之和最小,Bob会将前 x x x大的数都变为负,那么我们就只需要考虑Alice需要删除多少数字才能让结果数组之和最大。那么我们只需枚举 k k k次即可,对应Alice删除 0... k 0...k 0...k个数字,要使Bob取反后的数组之和最大,那么Alice删除数字将会按照从大到小的顺序。

标程

#define int long long
void Solved() {
    int n, k, x; cin >> n >> k >> x;
    vector<int> a(n + 1), b(n + 1);
    int res = -INF;
 
    for(int i = 1; i <= n; i ++ ) cin >> a[i];
    sort(a.begin() + 1, a.end());
    for(int i = 1; i <= n; i ++ ) {
        b[i] = a[i] + b[i - 1];
    }
 
    for(int i = n; i >= n - k; i -- ) {
        int j = max(0ll, i - x);
        res = max(res, b[j] - (b[i] - b[j]));
    }
 
    cout << res << endl;
}

Problems C. Partitioning the Array

思路

本题是一道同余数论。给定长度为 n n n的数组 a a a,然后将数组均分为若干份,如果当前分法可以找到每个子份对同一个数字取余后都相等,则分数加一,问能加多少分?

  1. 同余: 两个整数 a 、 b a、b ab,如果他们同时除以一个自然数 m m m,所得的余数相同,则称 a 、 b a、b ab对于模 m m m同余,记作 a ≡ b ( m o d . m ) a≡b(mod.m) abmod.m
  2. 关于将数组均分为若干份,我们可以直接枚举即可。
  3. 而判断当前所有组是否可以对同一个数字取余后相等,需要遍历一遍数组。

标程

void Solved() {
    int n; cin >> n;
    vector<int> a(n);

    for(auto &i : a) cin >> i;

    int res = 0;
    for(int i = 1; i <= n; i ++ ) {
        if(n % i == 0) {
            int g = 0;
            for(int j = i; j < n; j ++ ) {
                g = gcd(g, a[j] - a[j - i]);
            }
            res += (g != 1);
        }
    }
    
    cout << res << endl;   
}

Problems D. Array Repetition

思路

本题首先给出 n , q n,q n,q,然后给出 n n n次操作,每次操作有两种:1. 在数组后面添上一个数字 x x x,2. 将已有数组复制到数组后面 x x x次。然后有 q q q次查询,每次查询要求输出数组当前元素的值。

由于数组长度将会非常大,因此我们不能考虑模拟。

  1. 我们可以记录每次操作过后的数组长度。
  2. 每次查询是直接查询数组下标,因此我们可以找到第一个令数组长度大于该下标的操作。
  3. 注意当数据比较大时会爆 l o n g l o n g long long longlong,因此在读入的时候要注意。
  4. 如果将所有数据(操作、当前数组长度)一起存,然后二分查找,这样会导致超时。

我们可以在读入的时候记录该次操作后的数组长度,然后记录该操作的 x x x,用来最后输出。每次查询的时候找到对应的操作,若是第二种操作,则将本次查询的下标对上一次操作的数组长度取模。最后必然找出的是第一次操作,输出即可。

标程

void solve(){
    int n, q; cin >> n >> q;

    ll dp[n + 1] = {};
    int lstAdd[n + 1] = {};
    vector<int> pos;

    for (int i = 1, doAdd = true; i <= n; i++){
        int a, v; cin >> a >> v;

        if (a == 1) {
            lstAdd[i] = v;
            dp[i] = dp[i - 1] + 1;
        } else {
            lstAdd[i] = lstAdd[i - 1];
            dp[i] = ((v + 1) > 2e18 / dp[i - 1]) ? (ll)2e18 : dp[i - 1] * (v + 1);

            if (doAdd) pos.push_back(i);
        }
        if (dp[i] == 2e18) doAdd = false;
    }
    while (q--){
        ll k; cin >> k;

        for (int i = pos.size() - 1; i >= 0; i--){
            int idx = pos[i];

            if (dp[idx] > k && dp[idx - 1] < k){
                if (k % dp[idx - 1] == 0){
                    k = dp[idx - 1]; break;
                }
                k %= dp[idx - 1];
            }
        }
        cout << lstAdd[lower_bound(dp + 1, dp + n + 1, k) - dp]<<" \n"[q == 0];
    }
}

超时代码(思路较直观)

#define int long long
struct arr{int x, y, z;};
arr a[N];
int n, q, sum;
 
int find(int m) {//找第一个大于m的a[i].z
    int l = 1, r = n, mid;
    while(l < r) {
        mid = (l + r) / 2;
        if(a[mid].z < m) l = mid + 1;
        else r = mid;
    }
    return l;
}
 
void Solved() {
    cin >> n >> q;
    for(int i = 1; i <= n; i ++ ) {
        cin >> a[i].x >> a[i].y;
        if(a[i].x == 1) a[i].z = a[i - 1].z + 1;
        else {
            if((a[i].y > 2e18 / a[i - 1].z) && !sum){
                a[i].z = 2e18;
                sum = i;
            } else {
                a[i].z = a[i - 1].z * (a[i].y + 1);
            }
        }
    }
    while(q -- ) {
        int m; cin >> m;
        int t = find(m);
        while(a[t].x == 2) {
            m %= a[t - 1].z;
            if(m == 0) m = a[t - 1].z;
 
            t = find(m);
        }
        cout << a[t].y << " ";
    }
    cout << endl;
}
  • 37
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值