Codeforces Round #696 (Div. 2) A - D 解题报告

A. Puzzle From the

题意:

\(a\)\(b\) 两个数(长度相同)相加得到 \(c\),对于 \(c\) 中的如果某一段区间上的数字相同就省略只写一个(例如:\(112200\) 省略为:\(120\))就得到了 \(d\)。在已知 \(b\) 的情况下计算一个 \(a\) 要求使 \(d\) 最大。

思路:

显然对于一个数越长就越大,那么 \(c\) 的相邻位一定是不同的。第一位一定是 \(2\)

代码:
int main() {

    int t; cin >> t;
    while (t --) {
        int n;
        cin >> n;
        string a = "", b;
        cin >> b;
        int len_b = b.size();
        a += '1';
        for (int i = 1; i < len_b; i ++) {
            if (b[i] + '1' == a[i - 1] + b[i - 1]) a += '0';
            else a += '1';
        }
        cout << a << endl;
    }
    return 0;
}

B. Different Divisors

题意:

对于 \(a\) 的所有因数之差大于等于 \(d\)。求最小的满足条件的 \(a\)。(\(a\) 至少有四个因数)

思路:

对于一个数来说至少有两个因数那就是 \(1\) 和他本身。

那么我们从 \(1\) 开始,找一个 \(\ge d+1\) 的素数 \(x\)。在找一个 \(\ge x+d\) 的素数 \(y\)。那么 \(x\times y\) 就是答案。

之所以找素数是因为如果我们找的是合数,那么对于这个合数还有其他因子,那么就无法判断。

\(x \times y\) 根据质因数分解为:\(x^1 \times y^1\)。那么就只有这四个因数,显然 \(a - y \ge d\) 的。所以合理。

我们可以先预处理出素数,然后二分即可。

代码:
const int N = 1e7;
vector<int> primes;
int is_prime[N];

void init() {
    memset(is_prime, 0, sizeof is_prime);
    for (int i = 2; i < N; i ++) {
        if (is_prime[i] == 0) primes.pb(i);
        int len_primes = primes.size();
        for (int j = 0; j < len_primes && primes[j] * i  < N; j ++) {
            is_prime[i * primes[j]] = 1;
            if (i % primes[j] == 0) break;
        }
    }
}


int main() {

    init();
    
    int len_primes = primes.size();

    int t; cin >> t;
    while (t --) {
        int d; cin >> d;
        int a = lower_bound(all(primes), d + 1) - primes.begin();
        int b = lower_bound(all(primes), primes[a] + d) - primes.begin();
        ll ans = 1ll * primes[a] * primes[b];
        cout << ans << endl;
    }
    return 0;
}

C. Array Destruction

题意:

给定一个数组 \(a\)。初始选定一个 \(x\),在数组中找两个数 \(a, b\) 满足 \(a + b = x\),那么 \(a,b\) 就从数组中删去,并且 \(x = max(a, b)\),继续操作下去,问是否可以把数组中的数删光?如果可以就输出初始的 \(x\) 和每次删除的 \(a, b\)

思路:

显然我们可以知道对于当前的操作下选取的 \(a, b\) 中的较大值一定是当前数组中的最大值,只有这样才有可能删完。那么根据当前的 \(x\),又从数组中选出当前的最大值,那么另一个数就可以求出来。只有第一次操作的时候,由于 \(x\) 的未知,所以无法确定另一个数。

由于数组的长度 \(\le 2\times 10^3\)。那么我们可以暴力枚举第一次选取的另一个数。复杂度为 \(O(n^2)\)

代码:
const int N = 2e3 + 10;
map<int, int> mp;

int a[N];
vector<PII> ans;

int main() {

    int t; cin >> t;
    while (t --) {
        mp.clear();
        int n; cin >> n;
        int flag = 0;
        int m = n << 1;
        for (int i = 1; i <= m; i ++) {
            cin >> a[i];
            mp[a[i]] ++;
        }
        sort(a + 1, a + m + 1);
        mp[a[m]] --;
        for (int i = 1; i < m; i ++) {
            ans.clear();
            ans.pb({a[m], a[i]});
            int j = m - 1;
            map<int, int> tmp_map = mp;
            tmp_map[a[i]] --;
            int pre = a[m];
            while (j > 0) {
                if (tmp_map[a[j]]) {
                    if (pre - a[j] == a[j] && tmp_map[a[j]] >= 2) {
                        tmp_map[a[j]] -= 2;
                        ans.pb({a[j], a[j]});
                        pre = a[j];
                    } else if (pre - a[j] != a[j] && tmp_map[pre - a[j]]) {
                        tmp_map[a[j]] --;
                        tmp_map[pre - a[j]] --;
                        ans.pb({a[j], pre - a[j]});
                        pre = a[j];
                    } else {
                        break;
                    }
                }
                j --;
            }
            if (ans.size() == n) {
                flag = 1;
                break;
            }
        }
        if (flag) {
            puts("YES");
            cout << ans[0].x + ans[0].y << endl;
            for (int i = 0; i < n; i ++) cout << ans[i].x << " " << ans[i].y << endl;
        } else {
            puts("NO");
        }
    }
    
    return 0;
}

D. Cleaning

题意:

\(n\) 堆石头,每次你可以让相邻位上的石头数 \(-1\)(前提是有石头)。你有一次特殊操作是交换两堆相邻石头。问最终能否把石头的数量都变为 \(0\)

思路:

根据题意可知,第 \(x\) 堆石头只能由 \(x+1\) 堆来清除,直至第 \(n\) 堆清楚完第 \(n-1\) 堆后刚好为 \(0\)

那么 \(O(n^2)\) 的做法就是枚举交换的位置,然后判断是否可行即可。

此时我们可以发现在枚举交换位置后的判断有重复的计算,所以我们可以预处理出 \(l\)\(r\) 数组,\(l[i]\) 代表从左往右消除到位置 \(i\) 上剩余的石头数,\(r[i]\) 代表从右往左消除到位置 \(i\) 上的石头数量,如果 \(l[i] = r[i + 1]\) 就说明不用交换就可以消除完,那么我们枚举交换的位置 \(i, i + 1\),只需要判断四堆石头即:\(l[i - 1], a[i], a[i + 1], r[i + 2]\) 是否可以消除完即可。

注:当 \(l[i] < 0\),那么之后的都是不合法的了。

代码:
const int N = 2e5 + 10;

int a[N];
ll l[N], r[N];

int main() {

    int t; cin >> t;
    while (t --) {
        int n; cin >> n;
        memset(l, 0, sizeof l);
        memset(r, 0, sizeof r);
        int l_flag = n + 1, r_flag = -1;
        // LNF 为无穷大
        for (int i = 1; i <= n; i ++) {
            cin >> a[i];
            if (a[i] >= l[i - 1]) l[i] = a[i] - l[i - 1];
            else l[i] = LNF / 2;
        }   
        for (int i = n; i >= 1; i --) {
            if (a[i] >= r[i + 1]) r[i] = a[i] - r[i + 1];
            else r[i] = LNF;
        }
        int flag = 0;
        // 枚举交换的位置
        for (int i = 1; i <= n - 1; i ++) {
            if (l[i] == r[i + 1]) {
                flag = 1;
                break;
            }
            // 交换 i 和 i + 1
            if (a[i + 1] >= l[i - 1] && a[i] >= r[i + 2] && a[i + 1] - l[i - 1] == a[i] - r[i + 2]) {
                flag = 1;   
                break;
            }
        }
        if (flag == 1) puts("YES");
        else puts("NO");
    }
    return 0;
}
posted @ 2021-01-24 17:59  nonameless  阅读(78)  评论(0编辑  收藏  举报