1. 程式人生 > 實用技巧 >Codeforces Round #696 (Div. 2) (A~C題解)

Codeforces Round #696 (Div. 2) (A~C題解)

寫在前邊

連結:Codeforces Round #696 (Div. 2)

A. Puzzle From the Future

連結:A題連結

題目大意:

給定一個\(a\),\(b\)\(d = a + b\),例如:\(a = 0101\), \(b= 1111\), \(d = 1212\),但是d如果有連續重複的數字那麼會省掉,假如\(a + b = 12211\),則\(d = 121\),現在已知\(a\)\(b\)丟失了,要求求出一個\(b\)使得\(d\)最大。

思路

首先要使得\(d\)最大化,\(b\)的第一位必定是\(1\),剩下的直接從前到後模擬即可,若\(a_i + b_i == d[i-1]\)

那麼\(d\)會減小,因此“反其道而行之即可”。

程式碼:

#include <iostream>
#include <string>
#include <algorithm>
#include <cstdio>

using namespace std;

#define Inf 0x3f3f3f3f
#define PII pair<int, int>
#define PLL pair<long, long>

typedef long long LL;
typedef unsigned long long ULL;

void solve() {
    string s, res = "1";
    int n;
    cin >> n;
    cin >> s;
    for (int i = 1; i < s.size(); i++) {
        if (((s[i]-'0') + 1) == (s[i - 1] - '0' + res[i - 1] - '0')) {
            res += '0';
        } else {
            res += '1';
        }
    }

    cout << res << endl;
}

int main()
{
    int t;
    cin >> t;
    while (t--) {
        solve();
    }

    return 0;
} 

B. Different Divisors

連結:B題連結

題目大意:

給定一個\(d\),求\(a\),要求\(a\)至少有\(4\)個約數,並且約數兩兩之間的至少差\(d\),並且要求\(a\)是可行方案中中的最小值。

思路

\(\quad\) 題目要求四個約數,那麼讓其只有四個約數即可,即\(1 \quad p \quad q \quad pq\)\(p >= 1 + d\), \(q >= p + d\),因此\(p\)就是大於\(d + 1\)的最小質數,\(q\)就是大於\(p + d\)的最小質數,那麼\(p * q\)就是滿足條件\(a\)
證明:
\(\quad\)

\(p\)是一個\(>=1+d\)合數,那麼\(1 -- p\)之間必定還會會有一個\(p\)的約數\(p′\),因為\(p′\)\(p\)的約數,那麼它必然也是\(a\)的約數這就使得\(p′-1 < d\),不滿足約數之間至少差\(d\)的條件,因此矛盾。
\(\quad\)\(p\)不是最小質數,設其為\(p′\),另一個為\(q′\),那麼\(p′ * q′ > p*q\),因此\(p′\)\(q′\)並不是最優解。

綜上\(p\)\(p >= 1 + d\)的最小質數與\(q\)\(q >= p + d\)最小質數為最優解。

程式碼:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>

using namespace std;

#define Inf 0x3f3f3f3f
#define PII pair<int, int>
#define PLL pair<long, long>

typedef long long LL;
typedef unsigned long long ULL;
typedef vector<long long> VLL;
typedef vector<int> VI;

const int N = 25e3 + 10;
int primes[N], cnt;
bool st[N];

void get_primes() {
    for (int i = 2; i <= N; i++) {
        if (!st[i]) {
            primes[cnt++] = i;
        }
        for (int j = 0; primes[j] <= N / i; j++) {
            st[primes[j] * i] = true;
            if (i % primes[j] == 0) {
                break;
            }
        }
    }
}

void solve() {
    int d;
    cin >> d;
    int p, q, pcnt = 0;
    for (pcnt; pcnt <= cnt; pcnt++) {
        if (primes[pcnt] - 1 >= d) {
            p = primes[pcnt];
            break;
        }
    }
    for (int i = pcnt; i <= cnt; i++) {
        if (primes[i] - p >= d) {
            q = primes[i];
            break;
        }
    }
    cout << p * q << endl;
}

int main()
{
    int t;
    cin >> t;
    get_primes();
    while (t--) {
        solve();
    }
   
    return 0;
} 

C. Array Destruction

連結:C題連結

題目大意:

例如,如果最初\(a=[3,5,1,2]\),則可以選擇\(x=6\)。然後你可以選擇第二個和第三個元素的總和\(5+1=6\),並丟擲它們。在這個運算之後,\(x\)等於\(5\),陣列中有兩個元素:\(3\)\(2\)。你可以在下次操作時把它們扔了,那麼輸出

YES
6
1 5
2 3

若無論如何陣列內元素都無法清空,那麼輸出\(NO\)即可。

思路

每次選最大的數作為x。
證明:
假設\(a_i < a_j < a_k\),不選\(a_j\)來構造x,那麼即\(a_i + a_j = x\),此後\(x = a_j\),之後無論如何都消除不掉\(a_k\),因此每次選最大數作為x即可。

那麼第一次除了選最大的數作為下一輪的x,另一個數就需要列舉所有可能,直到列舉到一個可以消掉所有數的情況。明白了這一點那麼剩下的只需要模擬即可,可以用\(multiset\),因為其查詢刪除插入操作都是\(O(logn)\),若不用\(multiset\)則手寫二分查詢即可,最時間複雜度:\(O(n^2logn)\)

自己的錯誤:寫了一晚上,但最後還是\(TLE2\)告終,其實我已經知道了每次要選最大值了,但還是用雙指標來搜哪兩個數等於\(x\),由於答案唯一,因此搜的結果是對的,但是導致\(O(n^3)\)被卡,應該直接確定陣列中還剩下的最大數,然後剩下的小數直接用二分搜就好了。

程式碼:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <vector>
#include <set>

using namespace std;

#define Inf 0x3f3f3f3f
#define PII pair<int, int>
#define PLL pair<long, long>

typedef long long LL;
typedef unsigned long long ULL;
typedef vector<long long> VLL;
typedef vector<int> VI;

vector<PII> check(VI all, int n, int x) {
    vector<PII> res;
    multiset<int> s;
    for (auto it : all) {
        s.insert(it);
    }
    for (int i = 0; i < n; i++) { //2*n個數,n個答案
        auto it1 = s.end();
        it1--;
        int y = x - *it1;
        s.erase(it1);
        auto it2 = s.find(y);
        if (it2 == s.end()) {
            return {};
        }
        s.erase(it2);
        res.push_back({y, x - y});
        x = max(x - y, y);
    }
    return res;
}

void solve() {
    int n;
    cin >> n;
    VI all;
    for (int i = 0; i < n * 2; i++) {
        int c;
        cin >> c;
        all.push_back(c);
    }
    sort(all.begin(), all.end());
    for (int i = 0; i < all.size() - 1; i++) { //列舉第一次操作的第二個數
        int x = all[i] + all[all.size() - 1]; //最大的一個
        vector<PII> res = check(all, n, x);

        if (res.size()) {
            cout << "YES" << endl;
            cout << x << endl;
            for (auto it : res) {  
                cout << it.first << " " << it.second << endl; 
            }
            return;
        }
    }
    cout << "NO" << endl;
}

int main()
{
    int t;
    cin >> t;
    while (t--) {
        solve();
    }

    return 0;
}