codeforces 构造题专题(easy version)

1.CF1768C Elemental Decompress(*1300 贪心、构造)

参考@此处

\(s_1、s_2\)表示仍未填入\(p[]、q[]\)中的数字。

贪心策略:

将未重复的数尽量填入\(p[]\)中,重复的数填入\(q[]\)中,同时更新\(s_1、s_2\)

对于\(q[]\)中每一位未填的数,贪心选择\(s_2\)中最大的数且该数\(\leq p_i\)\(p[]\)的处理相同。

参考做法的代码相当优美,学到很多。

#include <bits/stdc++.h>

using namespace std;

#define endl '\n'
#define cerr(x) std::cerr << (#x) << " is " << (x) << '\n'
#define IOS std::ios::sync_with_stdio(false);std::cin.tie(nullptr);
#define PII pair<int, int>
#define pdd pair<double,double>
#define PLL pair<LL,LL>

#define LL long long

const double CLOCKS_PER_SECOND = ((clock_t) 1000);
const double CLOCKS_PER_MILLISECOND = ((clock_t) 1);
const int N = 2e5 + 10, M = 1e8, mod = 1e9 + 7, inf = 0x3f3f3f3f;
const double eps = 1e-6;
//#define x first
//#define y second

int T;


bool cmp(PII &a, PII &b) {
    return a.second < b.second;
}

bool cmp2(PII &a, PII &b) {
    return a.first < b.first;
}

void solve() {
    int n, Max = -1;
    cin >> n;
    vector<int> a(n);
    set<int> s1, s2;
    for (auto &ai: a) cin >> ai, Max = max(Max, ai);
    if (Max < n) {
        cout << "NO" << endl;
        return;
    }
    vector<int> p(n), q(n);
    for (int i = 1; i <= n; i++) s1.insert(i), s2.insert(i);
    for (int i = 0; i < n; i++) {
        if (s1.count(a[i])) s1.erase(p[i] = a[i]);
        else if (s2.count(a[i])) s2.erase(q[i] = a[i]);
        else {
            cout << "NO" << endl;
            return;
        }
    }

    for (int i = 0; i < n; i++) {
        if (!p[i]) {
            auto it = s1.lower_bound(q[i]);
            if (it != s1.end() && *it == q[i]) s1.erase(p[i] = *it);//找到了,而且值=q[i]
            else if (it == s1.begin()) {
                cout << "NO" << endl;
                return;
            } else {
                it = prev(it);
                s1.erase(p[i] = *it);
            }
        } else {
            auto it = s2.lower_bound(p[i]);
            if (it != s2.end() && *it == p[i]) s2.erase(q[i] = *it);
            else if (it == s2.begin()) {
                cout << "NO" << endl;
                return;
            } else {
                it = prev(it);
                s2.erase(q[i] = *it);
            }
        }
    }
    cout << "YES" << endl;
    for (int i = 0; i < n; i++) cout << p[i] << " \n"[i == n - 1];
    for (int i = 0; i < n; i++) cout << q[i] << " \n"[i == n - 1];
    return;
}


signed main() {
    IOS;
    cin >> T;
    while (T--)
        solve();
}

2A.Make Nonzero Sum (easy version) (*1300 贪心、构造)

因为不要求最小区间数,所以可以一个个拆开看。我们发现,\(a_1-a_2+a_3-a_4=(a_1-a_2)+(a_3-a_4)\),所以一个大区间一定能够拆分成\(len=2\)\(1\)的区间。故如果\(n\)为奇数是无解的。

讨论\(len=2\):如果两个数不相同,那么\(a_1-a_2\)不为\(0\),故我们贪心的将其分为\(\{a_1,a_1\}、\{a_2,a_2\}\),这样两者和为\(0;\)如果相同,则直接计入一个区间\(\{a_1,a_2\}\)

#include <bits/stdc++.h>

using namespace std;

#define endl '\n'
#define cerr(x) std::cerr << (#x) << " is " << (x) << '\n'
#define IOS std::ios::sync_with_stdio(false);std::cin.tie(nullptr);
#define PII pair<int, int>
#define pdd pair<double,double>
#define PLL pair<LL,LL>

#define LL long long


const double CLOCKS_PER_SECOND = ((clock_t) 1000);
const double CLOCKS_PER_MILLISECOND = ((clock_t) 1);
const int N = 2e5 + 10, M = 1e8, mod = 1e9 + 7, inf = 0x3f3f3f3f;
const double eps = 1e-6;
//#define x first
//#define y second

int T;

int a[N];

void solve() {
    int n;
    cin >> n;
    vector<PII > v;
    int sum = 0;
    for (int i = 1; i <= n; i++)
        cin >> a[i], sum += a[i];
    if (n & 1) {
        cout << -1 << endl;
        return;
    }
    for (int i = 1; i <= n; i += 2) {
        if (a[i] == a[i + 1]) v.push_back({i, i + 1});
        else v.push_back({i, i}), v.push_back({i + 1, i + 1});
    }
    cout << v.size() << endl;
    for (auto &i: v) cout << i.first << " " << i.second << endl;
    return;
}


signed main() {
    IOS;
    cin >> T;
    while (T--)
        solve();
}

2B.Make Nonzero Sum (hard version)(*1500 贪心、构造)

首先,可以判断出:若非\(0\)的个数为奇数时无解,原因与\(2A\)相同。

\(\{1,1\}、\{-1,-1\}\)的情况不用再重复,我们判断如下情况:

  • \(\{1,0...0,1\}\):可以拆分成\(\{1\}+\{0\}+...+\{0,1\}=0\)

  • \(\{1,0...0,-1\}\):可以拆分成\(\{1\}+\{0\}+...+\{-1\}=0\)

前置\(0\)全部单独划分为一个区间。

#include <bits/stdc++.h>

using namespace std;

#define endl '\n'
#define cerr(x) std::cerr << (#x) << " is " << (x) << '\n'
#define IOS std::ios::sync_with_stdio(false);std::cin.tie(nullptr);
#define PII pair<int, int>
#define pdd pair<double,double>
#define PLL pair<LL,LL>

#define LL long long


const double CLOCKS_PER_SECOND = ((clock_t) 1000);
const double CLOCKS_PER_MILLISECOND = ((clock_t) 1);
const int N = 2e5 + 10, M = 1e8, mod = 1e9 + 7, inf = 0x3f3f3f3f;
const double eps = 1e-6;
//#define x first
//#define y second

int T;

int a[N];

void solve() {
    int n;
    cin >> n;
    vector<PII > v;
    int sum = 0;
    for (int i = 1; i <= n; i++)
        cin >> a[i], sum += (a[i] != 0);
    if (sum & 1) {
        cout << -1 << endl;
        return;
    }
    for (int i = 1; i <= n;) {
        while (!a[i] && i <= n) {
            v.push_back({i, i});
            i++;
        }
        if (i > n) break;
        int l = i, r = i + 1;
        if (a[r]) {
            if (a[l] == a[r]) v.push_back({l, l + 1});
            else {
                v.push_back({l, l});
                v.push_back({l + 1, l + 1});
            }
        } else {
            v.push_back({l, l});
            while (!a[r] && r <= n) {
                v.push_back({r, r});
                r++;
            }
            if (a[l] != a[r]) v.push_back({r, r});
            else v.back().second = r;//更改为{0,1},凑成(1)+(0-1)
        }//{1,1},{0,0},1
        i = r + 1;

    }
    cout << v.size() << endl;
    for (auto &i: v) cout << i.first << " " << i.second << endl;
    return;
}


signed main() {
    IOS;
    cin >> T;
    while (T--)
        solve();
}

3.Vlad and a Pair of Numbers(*1400 位运算,构造)

\[\frac{a+b}{2}=a\oplus b=x \]

由常见的推论得:\(a+b=a \oplus b+((a \and b)<<1)\)

故化简得:\(a \oplus b=((a \and b)<<1)=x\)

分析\((x)_2\)的取值:

  • \(1.x\)的第\(i\)位为\(1\),第\(i-1\)位为\(1\),不成立

  • \(2.x\)的第\(i\)位为\(1\),第\(i-1\)位为\(0\),成立,此时\(a\)\(b\)\(i\)位不相同即满足;

  • \(3.x\)的第\(i\)位为\(0\),第\(i-1\)位为\(1\),成立,此时\(a\)\(b\)\(i\)位均为\(1\)时满足;

  • \(4.x\)的第\(i\)位为\(0\),第\(i-1\)位为\(0\),成立,此时\(a\)\(b\)\(i\)位均为\(0\)时满足;

分析可知,无解的情况当且仅当\((x)_2\)有连续两位为\(1\)。故对于可行解\(x\)一定是满足\(1 \ 0 \ 1 \ 0\)排列的。

不妨如下构造:

\((n)_2[i]=1,a[i]=1,b[i]=0,a[i-1]=b[i-1]=1\)

#include <bits/stdc++.h>

using namespace std;

#define endl '\n'
#define cerr(x) std::cerr << (#x) << " is " << (x) << '\n'
#define IOS std::ios::sync_with_stdio(false);std::cin.tie(nullptr);
#define PII pair<int, int>
#define pdd pair<double,double>
#define PLL pair<LL,LL>
#define rep(i, j, k) for(int i=j;i<=k;i++)

#define int long long


const double CLOCKS_PER_SECOND = ((clock_t) 1000);
const double CLOCKS_PER_MILLISECOND = ((clock_t) 1);
const int N = 1e6 + 10, M = 1e8, mod = 1e9 + 7, inf = 0x3f3f3f3f;
const double eps = 1e-6;
//const int fac[] = {1, 1, 2, 6, 24, 120, 720, 5040, 40320, 362880};
const int dx[] = {-1, 0, 0, 1}, dy[] = {0, 1, -1, 0};
//#define x first
//#define y second
//int fac[N];

int qpow(int a, int b) {
    int ans = 1LL, base = a;
    while (b) {
        if (b & 1) ans = (ans * base) % mod;
        base = (base * base) % mod;
        b >>= 1;
    }
    return ans;
}
/*
int C(int n, int k) {
    if (k > n) return 0;
    return (fac[n] * qpow(fac[k], mod - 2) % mod) * qpow(fac[n - k], mod - 2) % mod;
}

int Lucas(int n, int k) {
    if (!k) return 1LL;
    return C(n % mod, k % mod) * Lucas(n / mod, k / mod) % mod;
}
*/
int T;
int fac[32 + 5];

void solve() {
    int n;
    cin >> n;
    vector<int> v(32 + 5), a(35, 0), b(35, 0);
    if (n & 1) {
        cout << -1 << endl;
        return;
    }
    int t = n, tot = 0;
    while (t) {
        v[tot++] = t % 2, t /= 2;
    }
    for (int i = 0; i < tot - 1; i++) {
        if (v[i] & v[i + 1]) {
            cout << -1 << endl;
            return;
        }
    }
    for (int i = 0; i < tot; i++) {
        if (v[i]) {//(n)2=1 0
            a[i] = 1, b[i] = 0;
            a[i - 1] = b[i - 1] = 1;
        }
    }
    int numa = 0, numb = 0;
    for (int i = 0; i < tot; i++) {
        if (a[i]) numa += fac[i];
        if (b[i]) numb += fac[i];
    }
    if ((numa ^ numb) != ((numa & numb) << 1)) {
        cout << -1 << endl;
        return;
    }
    cout << numa << " " << numb << endl;
}


signed main() {
    IOS;
    for (int i = 0, num = 1; i < 32 + 5; i++, num *= 2) fac[i] = num;
    cin >> T;
    while (T--)
        solve();
}
posted @ 2023-01-20 14:45  SxtoxA  阅读(70)  评论(0)    收藏  举报
12 13