鱼香rose'Blog

Codeforces Round 960 (Div

\(\Huge{ Codeforces Round 960 (Div. 2)}\)

题目链接:Codeforces Round 960 (Div. 2)

Problems A. Submission Bait

题意

给出一个数组,Alice和Bob每次可以选择一个数字,且每次选择的数字不小于上一个人选择的数字。然后将其变为0。

最后无法进行选择的人输掉比赛。

思路

Alice先手,若最大数的个数为奇数,那么Alice赢,若为偶数,考虑第二大数字的个数,以此类推。

标程

#include<bits/stdc++.h>

using namespace std;
void Solved() {
    int n; cin >> n;
    vector<int> a(n + 1);

    for(int i = 1; i <= n; i ++ ) cin >> a[i];
    
    sort(a.begin() + 1, a.end(), [](int n1, int n2) {
        return n1 > n2;
    });

    int x = 0, mx = a[1], z = 1;
    for(int i = 1; i <= n;  i ++ ) {
        if(mx == a[i]) x ++;
        else {
            if(x % 2 == z) {
                cout << "YES\n"; return;
            } else {
                x = 1; mx = a[i];
            }
        }
    }

    if(x % 2 == z) cout << "YES\n";
    else cout << "NO\n"; 
}

signed main(void) {
    int ALL = 1; 
    cin >> ALL;
    while(ALL -- ) Solved();

    return 0;
}

Problems B. Array Craft

题意

给出数组b,然后给出两个定义:

  1. 最大前缀位置:满足\(b_1+\ldots+b_i=\max_{j=1}^{m}(b_1+\ldots+b_j)\)的最小索引\(i\)
  2. 最大后缀位置:满足$$b_i+\ldots+b_m=\max_{j=1}^{m}(b_j+\ldots+b_m)$$的最小索引\(i\)

然后给出三个整数\(n,x,y\),分别表示数组大小,最大前缀位置,最大后缀位置。

思路

根据\(x,y\)的相对位置直接模拟就行。

根据定义,\(a_X=a_y=1\),对于\(x\),从\(x\)开始的\((1\le i \le x)\)的前缀和必然大于\(1\),从\(x\)开始的\((x\le i \le n)\)的前缀和必然小于\(1\)\(y\)也同理。

标程

#include<bits/stdc++.h>

using namespace std;

void Solved() {
    int n, x, y; cin >> n >> x >> y;

    vector<int> a(n + 1);

    a[x] = a[y] = 1;
    if(x <= y) {
        int x1 = 0;
        for(int i = x; i >= 1; i -- ) {
            if(x1 < 1) a[i] = 1, x1 ++;
            else a[i] = -1, x1 --;
        }
        for(int i = x + 1; i < y; i ++ ) {
            a[i] = -1;
        }
        x1 = 0;
        for(int i = y; i <= n; i ++ ) {
            if(x1 < 1) a[i] = 1, x1 ++;
            else a[i] = -1, x1 --;
        }
    } else {
        int x1 = 1;
        for(int i = y - 1; i >= 1; i -- ) {
            if(x1 >= 1) a[i] = -1, x1 --;
            else a[i] = 1, x1 ++;
        }
        for(int i = y + 1; i < x; i ++ ) {
            a[i] = 1;
        }
        x1 = 1;
        for(int i = x + 1; i <= n; i ++ ) {
            if(x1 >= 1) a[i] = -1, x1 --;
            else a[i] = 1, x1 ++;
        }
    }

    for(int  i= 1 ; i <= n; i ++ ) {
        cout << a[i] << ' ';
    } cout << endl;
}

signed main(void) {
    int ALL = 1; 
    cin >> ALL;
    while(ALL -- ) Solved();

    return 0;
}

Problems C. Mad MAD Sum

题意

给出定义:\(MAD\)(最大显示重复数)在数组中出现至少两次的最大数。具体来说,如果没有出现至少两次的数字,则\(MAD\)\(0\)

循环执行下列操作:

  1. \(sum=sum+\sum_{i=1}^{n}{a_i}\)
  2. \(b_i=MAD(a_1,a_2…a_n)\)
  3. \(a_i=b_i(1\le i \le n)\)

当数组\(a\)全为0时结束循环,求sum的大小。

思路

手动模拟一次即可发现,只需一次循环,数组a就会变为不严格的升序\(a_i\le a_{i + 1}(1\le i\le n-1)\)

设第一次手动模拟后的数组为b_1,那么b_1中除了最后一位,其他位可能会出现单个数字的情况(即相邻两个数都不同),且现在b_i为升序,那么这一位在下一次循环后就不存在了。

但第二次循环之后,b_2除最后一位,其他位不可能出现上述情况。

b_1之所以会出现单个数字的情况,是因为循环前原数组可能是无序的。

b_2不会出现这种情况就是因为b_1是有序的。

规律可以观察下列两种情况:

\(a:[4,5,3,3,3,3,4,5]\) \(a:[4,4,5,5,6,6]\)
\(b_1:[0,0,0,3,3,3,4,5]\) \(b_1:[0,4,4,5,5,6]\)
\(b_2:[0,0,0,0,3,3,3,3]\) \(b_2:[0,0,4,4,5,5]\)
\(b_3:[0,0,0,0,0,3,3,3]\) \(b_3:[0,0,0,4,4,5]\)
\(b_4:[0,0,0,0,0,0,3,3]\) \(b_4:[0,0,0,0,4,4]\)
\(b_5:[0,0,0,0,0,0,0,3]\) \(b_5:[0,0,0,0,0,4]\)

通过规律,我们可以先处理出来前两次循环,然后直接计算即可

标程

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

#define int long long 

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

    for(int i = 1; i <= n; i ++ ) cin >> a[i];

    int mx = 0, res = 0;
    for(int j = 0; j < 2; j ++ ) {
        map<int, int> mp;
        mx = 0;
        for(int i = 1; i <= n; i ++ ) {
            res += a[i];
            mp[a[i]] ++;
            if(mp[a[i]] >= 2) mx = max(mx, a[i]);
            a[i] = mx;
        }
    }
    for(int i = 1; i <= n; i ++ ) {
        res += a[i] * (n - i + 1);
    }
    
    cout << res<< endl;    
}

signed main(void) {
    int ALL = 1; 
    cin >> ALL;
    while(ALL -- ) Solved();

    return 0;
}

Problems D. Grid Puzzle

题意

给出一个数字n,然后给出一个长度为\(n\)的数组\(a\)。数组a表示在一个大小为\(n\times n\)的矩阵中,第i行的前\(a_i\)各元素是黑色。

先有下列两种操作,每次只能选择一个执行:

  1. 选择一个\(2\times 2\)的矩阵,并将其变为白色。
  2. 选择任意一行,将其全变为白色。

问最少操作多少次,可以将整个\(n\times n\)的矩阵全变为白色。

思路

  1. 容易发现,如果当前行\(a_i > 4\),那么显然使用一次操作2最优。

  2. 如果相邻两行\(a_i \le 2\),那么使用操作1最优,因为可能消除下一行。

  3. 如果当前的\(a_i\le 4\),那么可能用操作1,也可能是操作2;此时若下一行\(a_{i+1}\le 4\),那么考虑当前使用操作1。

综上,我们可以用\(f_i\)表示前i行的最优解,\(g_i\)记录上述(3)的情况,然后进行状态转移即可。最后\(f[n]\)即为结果。

标程

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

const int mod = 1e9 + 7;

void Solved() {
    int n; cin >> n;
    vector<int> a(n + 1), f(n + 1), g(n + 1, mod);;

    int res = 0;
    for(int i = 1; i <= n; i ++ ) cin >> a[i];
    
    for(int i = 1; i <= n; i ++ ) {
        f[i] = f[i - 1] + (a[i] > 0);                   //默认都用操作2
        if(a[i] <= 2) {                                 //这类情况可以判断是否用操作1
            g[i] = min(g[i], f[i - 1] + 1);
            f[i] = min(f[i], g[i - 1]);					//考虑将上一行的染色方式改为操作1
        }
        if(i + 1 <= n && a[i] <= 4 && a[i + 1] <= 4) {  //这类情况用两次操作1最优
            g[i + 1] = g[i - 1] + 2;
        }
    }
    
    cout << f[n]<< endl;
}

signed main(void) {
    int ALL = 1; 
    cin >> ALL;
    while(ALL -- ) Solved();

    return 0;
}
posted @ 2026-01-15 21:47  鱼香_rose  阅读(1)  评论(0)    收藏  举报