Codeforces Round #696 (Div. 2)部分题解(A,B,C,D)

oj: CodeForces

题目解析采用倒叙

D. Cleaning

oj: CodeForces

题意

给你一个包含 n n n 个元素的数组,你可以执行以下操作任意次:

选择两个挨着的正整数,使他们分别减一。

注意当一个数字减为 0 0 0 之后,他旁边的两个数字并不相邻。

你可以选择在开始前做一次额外的操作:选择两个相邻的数字使之互换位置。

判断是否有可能把数组全部减为 0 0 0

题解

假设数组为 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an,当消去 a 1 a_1 a1 a 2 a_2 a2 变成 a 2 − a 1 a_2-a_1 a2a1。当消去前两项后 a 3 a_3 a3 变成 a 3 − a 2 + a 1 a_3-a_2+a_1 a3a2+a1,以此类推。

我们把消去前 i − 1 i-1 i1 项时的第 i i i 项定义为 b b b 数组。那么要想使数组全部消去,需要保证 b b b 数组的每一个元素都大于等于 0 0 0,且 b b b 数组的最后一个元素是 0 0 0

首先检查不交换能不能消去全部的元素。

然后考虑交换两个数字后的影响,假设现在交换 a i a_i ai a i + 1 a_{i+1} ai+1,这次交换对前面的数字是没有影响的;对后面的影响分奇偶性考虑。

i i i 项由 a i a_i ai 变成 a i + 1 a_{i+1} ai+1,增加值为 a i + 1 − a i a_{i+1}-a_i ai+1ai,第 i i i 项后面的元素的增加值是 2 ( a i − a i + 1 ) 2(a_i-a_{i+1}) 2(aiai+1) − 2 ( a i − a i + 1 ) -2(a_i-a_{i+1}) 2(aiai+1) 随着奇偶性交替出现。和 i + 1 i+1 i+1 相同奇偶性的增加值是 2 ( a i − a i + 1 ) 2(a_i-a_{i+1}) 2(aiai+1),否则是 − 2 ( a i − a i + 1 ) -2(a_i-a_{i+1}) 2(aiai+1)

那么我们按照奇偶性保存后缀的最小值,然后枚举交换的数字,看最小值加上对应奇偶性位置的增加值是否小于 0 0 0 即可。如果不小于 0 0 0 就说明交换后后面的 b b b 数组元素都不小于 0 0 0,否则就说明后面没办法全部消去。而且由于交换只对后面的数字有效,所以交换 i i i 位置和 i + 1 i+1 i+1 位置的数字时,必须保证 i i i 前面的 b b b 数组元素都是大于等于 0 0 0 的。

综上,我们需要枚举交换的位置,且交换的位置不能在 b b b 数组中的负数位置之后。枚举时需要保证 a i + 1 − a i ≥ 0 a_{i+1}-a_i\ge 0 ai+1ai0,对应奇偶性位置的最小值加上增加值大于等于 0 0 0 b b b 数组最后一个元素为 0 0 0

代码

#include <bits/stdc++.h>
#define _for(i, a) for(int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(int i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
typedef long long LL;
const int maxn = 200005;

inline int read() {
    
    
    int x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') {
    
     if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') {
    
     x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int n;
int a[maxn];                // 原数组
int b[maxn];                // 消去所有前面的元素后自己的剩余值
int mi[maxn];               // 对应奇偶性位置的后缀最小值

int sol() {
    
    
    _for(i, n) a[i] = read(), b[i] = a[i];
    int f = 1;              // 标记是否可以不用任何而交换就消去所有的元素
    for(int i = 1; i < n; ++i) {
    
    
        b[i] -= b[i - 1];
        if(b[i] < 0) f = 0;
    }
    if(f && b[n - 1] == 0) return 1;    // 可以直接消去所有的元素,返回成功
    mi[n - 1] = b[n - 1];
    mi[n - 2] = b[n - 2];
    for(int i = n - 3; i >= 0; --i) mi[i] = min(mi[i + 2], b[i]);   // 求出对应奇偶性位置的后缀最小值
    int p = 0;
    while(p + 2 < n && b[p] >= 0) ++p;      // 由于交换只对i及i后面的元素有影响,所以必须保证i前面的b大于等于0
    for(int i = 0; i <= p; ++i) {
    
    
        int t = a[i] - a[i + 1];            // 交换形成的差
        if(b[i] - t < 0) continue;
        if(mi[i + 1] + 2 * t >= 0 && (i + 2 < n && mi[i + 2] - 2 * t >= 0 || i + 2 >= n)) {
    
     // 如果交换后后面所有的b都大于等于0则有可能成功
            if(b[n - 1] + ((i & 1) == ((n - 1) & 1) ? -1 : 1) * 2 * t == 0) return 1;       // 如果交换后满足上面的条件并且最后一个b为0,就代表可以全部消去
        }
    }
    return 0;
}

int main() {
    
    
    int T = read();
    _for(i, T) {
    
    
        n = read();
        printf("%s\n", sol() ? "YES":"NO");
    }
    return 0;
}

C. Array Destruction

oj: CodeForces

题意

给你一个长度为 2 n 2n 2n 的数组,最开始你可以选择一个任意的正整数 x x x。然后反复执行以下操作:

从数组的剩余元素中选出两个,并且这两个数的和要等于 x x x

让这两个数中较大的那个作为新的 x x x

判断是否有可能把数组中全部的元素消去。如果有可能就输出一个合法的方案。

题解

首先第一轮中的两个数字之一是可以确定的,第一轮选的两个数一定包含数组的最大值,第二个则不确定。且之后的每一轮一定都包含数组的剩余元素的最大值。

并且一旦前一轮的两个值确定后,后一轮的数字也是确定的。假设上一轮选的较大的数是 a a a,那么这一轮要选的数字就是剩下的最大值 m m m a − m a-m am。那么只要第一轮的数字确定后,后面所有的递推过程都已经确定了。

那么唯一不确定的就是第一轮选的除了最大值的另一个数。我们只需要枚举第一轮选的那个数字,然后尝试递推一遍试试能不能成功即可。

至于如何尝试递推,先对a数组排个序,统计一下每个数字的出现次数,然后从大到小选即可。

总时间复杂度 O ( n 2 ) O(n^2) O(n2)

代码

#include <bits/stdc++.h>
#define _for(i, a) for(int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(int i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
typedef long long LL;

inline int read() {
    
    
    int x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') {
    
     if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') {
    
     x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int n;
vector<int> a;
int vis[1000005];
vector<int> path;

void init() {
    
    
    a.clear();
    path.clear();
}

int che(int sum) {
    
    
    int p = 2 * n - 2;
    _for(i, n - 1) {
    
    
        while(p >= 0 && !vis[a[p]]) --p;
        if(p < 0) return 0;
        path.push_back(a[p]);
        --vis[a[p]];
        if(!vis[sum - a[p]]) return 0;
        path.push_back(sum - a[p]);
        --vis[sum - a[p]];
        sum = a[p];
    }
    return 1;
}

void sol() {
    
    
    init();
    _for(i, 2 * n) a.push_back(read()), ++vis[a[i]];
    sort(a.begin(), a.end());
    int ans = 0;
    _for(i, 2 * n - 1) {
    
    
        while(path.size()) {
    
    
            ++vis[path.back()];
            path.pop_back();
        }
        path.push_back(a.back());
        --vis[a.back()];
        --vis[a[i]];
        path.push_back(a[i]);
        if(ans = che(a.back())) break;
    }
    if(ans) {
    
    
        printf("YES\n%d\n", path[0] + path[1]);
        for(int i = 0; i < path.size(); i += 2) printf("%d %d\n", path[i], path[i + 1]);
    }
    else printf("NO\n");
    _for(i, 2 * n) vis[a[i]] = 0;
}

int main() {
    
    
    int T = read();
    _for(i, T) {
    
    
        n = read();
        sol();
    }
    return 0;
}

B. Different Divisors

oj: CodeForces

题意

找出满足以下条件的最小的a:

  • a至少有4个除数。
  • a的任意两个除数的差至少是d。

题解

首先不管 a a a 的值是什么, 4 4 4 个除数中第一个除数可以是 1 1 1,第 4 4 4 个除数可以是 a a a 本身。所以我们只需要找出另外两个除数就能确定 a a a 了。

要使 a a a 尽可能小,则 a a a 的除数也需要尽可能小。所以第 2 2 2 个除数是和 1 1 1 相差至少 d d d 的第一个素数。

至于为什么是素数,如果 a a a 可以被 c c c 整除,那么 c c c 需要和 1 1 1 至少相差 d d d,考虑到我们取尽可能小的数作为除数,则我们将 c c c 作为第 2 2 2 个除数,如果 c c c 也不是素数,则 c c c 的除数需要和 1 1 1 至少相差 d d d ,则我们将 c c c 的除数作为第 2 2 2 个除数,由此循环,我们一定取的是个素数作为第 1 1 1 个除数。

3 3 3 个除数是和第 2 2 2 个除数相差至少为 d d d 的第一个素数。

所以答案是第 2 2 2 个除数乘以第 3 3 3 个除数。

代码

#include <bits/stdc++.h>
#define _for(i, a) for(int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(int i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
typedef long long LL;
const int maxn = 100005;

inline int read() {
    
    
    int x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') {
    
     if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') {
    
     x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

vector<int> arr;
int vis[1000006];
void doit(int maxnum) {
    
    
    for(int i = 2; i <= maxnum; ++i) {
    
    
        if(!vis[i]) arr.push_back(i);
        for(int j = 0; j < arr.size() && arr[j] * i <= maxnum; ++j) {
    
    
            vis[arr[j] * i] = 1;
            if(i % arr[j] == 0) break;
        }
    }
}

int d;

int main() {
    
    
    doit(1000001);
    int T = read();
    _for(i, T) {
    
    
        d = read();
        LL a = *lower_bound(arr.begin(), arr.end(), d + 1);
        LL b = *lower_bound(arr.begin(), arr.end(), a + d);
        printf("%lld\n", a * b);
    }
    return 0;
}

A. Puzzle From the Future

oj: CodeForces

题意

定义一种运算:给定两个包含前导 0 0 0 的只由 0 0 0 1 1 1 构成的整数 a a a b b b,可以求出他们的和 d d d,例如 a = 0110 a=0110 a=0110, b = 1101 b=1101 b=1101, d = a + b = 0110 + 1101 = 1211 d=a+b=0110+1101=1211 d=a+b=0110+1101=1211,然后将 d d d 的相邻的相同元素合到一起,那么 1211 1211 1211 就变成了 121 121 121

给定整数 b b b,求出 可以求出最大的 d d d a a a

题解

和一旦被合并了某些元素,数量级就直接变小了,这对大小的影响是最大的,所以求和的时候要避免任何元素的合并。

其次权越大的位对结果的影响越大,所以让权较大的位的数值尽可能大,然后让权小的位避让权大的位使得和不会被合并。

从左到右枚举 b b b

  • b [ i ] b[i] b[i] 1 1 1
    • d [ i − 1 ] d[i-1] d[i1] 不为 2 2 2 时, a a a 1 1 1 d [ i ] d[i] d[i] 2 2 2
    • 否则 a a a 0 0 0 d [ i ] d[i] d[i] 1 1 1
  • b [ i ] b[i] b[i] 0 0 0
    • d [ i − 1 ] d[i-1] d[i1] 不为 1 1 1 时, a a a 1 1 1 d [ i ] d[i] d[i] 1 1 1
    • 否则 a a a 0 0 0 d [ i ] d[i] d[i] 0 0 0

代码

#include <bits/stdc++.h>
#define _for(i, a) for(int i = 0, lennn = (a); i < lennn; ++i)
#define _rep(i, a, b) for(int i = (a), lennn = (b); i <= lennn; ++i)
using namespace std;
typedef long long LL;
const int maxn = 100005;

inline int read() {
    
    
    int x(0), f(1); char ch(getchar());
    while (ch<'0' || ch>'9') {
    
     if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0'&&ch <= '9') {
    
     x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

int n;
char s[maxn];

void sol() {
    
    
    scanf("%s", s);
    int tem = -1;
    string ans = "";
    _for(i, n) {
    
    
        if(s[i] == '1') {
    
    
            if(tem != 2) ans += '1', tem = 2;
            else ans += '0', tem = 1;
        }
        else {
    
    
            if(tem != 1) ans += '1', tem = 1;
            else ans += '0', tem = 0;
        }
    }
    cout << ans << "\n";
}

int main() {
    
    
    int T = read();
    _for(i, T) {
    
    
        n = read();
        sol();
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_42856843/article/details/112971202