构造题合集

简介

构造题是一种有趣的题目类型,一般会给定几个条件,要求构造出符合这些条件的数字/序列等,与数学关联较大

题目

CF1603B.Moderate Modular Mode

题目链接CF1603B

题意

给定 \(2\) 个偶数 \(x,y\) ,找到一个整数 \(n\) 满足 \(n\bmod x=y\bmod n\)

分析

我们从 \(x,y\) 的大小关系分类考虑。若 \(x=y\) ,显然可以取 \(n=x\) 。若 \(x>y\) ,可以取 \(n=x+y\) ,此时 \((x+y)\bmod x=y\bmod(x+y)=y\) 。若 \(x<y\) ,发现此时难以找到一个符合条件的 \(n\) ,可以借助图像:

\(p=kx\)\((k+1)x>y\) ,那么 \(p=y-y\bmod x\) ,如果取 \(p\)\(y\) 的中点为 \(n\) ,可以得出 \(n\bmod x=|PN|,y\bmod n=|NY|\) ,满足 \(n\bmod x=y\bmod n\) ,所以 \(n=y-\frac{1}{2}(y\bmod x)\)

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

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int t;
    cin >> t;
    while(t--) {
        int x, y;
        cin >> x >> y;
        if(x > y)
            cout << x + y << endl;
        else if(x == y)
            cout << x << endl;
        else
            cout << y - (y % x) / 2 << endl;
    }
    return 0;
}

CF1608B.Build the Permutation

题目链接CF1608B

题意

给定 \(3\) 个整数 \(n,a,b\) 判断是否存在满足以下条件的 \(1\sim n\) 的排列:

  • 有且仅有 \(a\) 个在区间 \([2,n-1]\) 内的整数 \(i\) 满足 \(p_{i-1}<p_i,p_i>p_{i+1}\) (local maximum)
  • 有且仅有 \(b\) 个在区间 \([2,n-1]\) 内的整数 \(i\) 满足 \(p_{i-1}>p_i,p_i<p_{i+1}\) (local minimum)

如果存在这样的排列,输出它们中的任意一个,如果不存在,输出 \(-1\)

分析

考虑何时存在这样的排列。数列单调性不变时,既不会出现local maximum也不会出现local minimum,若数列单调性改变,那么必然出现它们中的一个。两个local maximum之间单调性一定发生了改变,所以必然会出现一个local minimum,同理两个local minimum之间也一定会有一个local maximum,所以 \(|a-b|\leq 1\) 。由于区间 \([2,n-1]\) 总共有 \(n-2\) 个不同整数,所以 \(a+b\leq n-2\)

再考虑存在时如何构造

  • \(a<b\) ,可以构造:

    \[2,1,4,3,\cdots,2a+2,2a-1,2a+3,2a+4,\cdots,n-1,n \]

  • \(a\geq b\) ,设 \(t=n-a-b-2\) ,可以构造:

    \[1,2,\cdots,t,t+1,t+1+\lfloor\frac{a+b+3}{2}\rfloor,t+2,t+2+\lfloor\frac{a+b+3}{2}\rfloor,\cdots \]

奇偶讨论后容易证明这两种构造是满足条件的

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

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int t;
    cin >> t;
    while(t--) {
        int n, a, b;
        cin >> n >> a >> b;
        if(a + b + 2 > n || abs(a - b) > 1) {
            cout << -1 << endl;
            continue;
        }
        if(a >= b) {
            for(int i = 1; i <= n - a - b - 2; i++)
                cout << i << ' ';
            for(int i = 1; i <= a + b + 2; i++)
                cout << n - a - b - 2 + (i & 1 ? (i + 1) >> 1 : (i >> 1) + (a + b + 3) / 2) << ' ';
        } else {
            for(int i = 1; i <= 2 * a + 2; i++)
                cout << (i & 1 ? i + 1 : i - 1) << ' ';
            for(int i = 2 * a + 3; i <= n; i++)
                cout << i << ' ';
        }   
        cout << endl;
    }
    return 0;
}

CF1650D.Twist the Permutation

题目链接CF1650D

题意

题目较复杂,参考原文

分析

给定了经过 \(n\) 轮后的数组,那么我们考虑第 \(i-1\) 轮数组如何变为第 \(i\) 轮数组。可以发现若 \(i\) 在第 \(i\) 轮数组中的位置为 \(j\) ,那么第 \(i-1\) 轮数组只要对 \(i\) 进行 \(j\) 次转换即可得到这一结果。所以 \(i\) 依次从 \(n\) 取到 \(1\) ,每次都可以判定第 \(i-1\) 轮数组到第 \(i\) 轮数组用了多少次转换,之后再还原回第 \(i-1\) 轮数组,重复这一过程即可

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

int ans[2000 + 5];
int a[2000 + 5];

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int t;
    cin >> t;
    while(t--) {
        memset(ans, 0, sizeof(ans));
        int n;
        cin >> n;
        for(int i = 1; i <= n; i++)
            cin >> a[i];
        for(int i = n; i >= 1; i--) {
            int idx;
            for(int j = 1; j <= i; j++) {
                if(a[j] == i) {
                    idx = j;
                    break;
                }
            }
            vector<int> v;
            for(int j = 1; j <= i - 1; j++) 
                v.push_back(a[(idx + j - 1) % i + 1]);
            for(int j = 0; j < i - 1; j++)
                a[j + 1] = v[j];
            ans[i] = (idx == i ? 0 : idx);
        }
        for(int i = 1; i <= n; i++)
            cout << ans[i] << ' ';
        cout << endl;
    }
    return 0;
}

CF1635C.Differential Sorting

题目链接CF1635C

题意

给定一个长度为 \(n\) 的数组 \(a\) ,可以执行如下操作:

  • 任取三个整数 \(x,y,z(1\leq x<y<z\leq n)\) ,使 \(a_x=a_y-a_z\)

求如何操作能使数组变为非减序列,即 \(\forall i,a_i\leq a_{i+1}\)

不要求操作数最小

分析

考虑序列的最后三个数 \(a_n,a_{n-1},a_{n-2}\) ,由上述规则可知 \(a_{n-1}\)\(a_n\) 无法更改,如果 \(a_{n-1}>a_n\) ,那么无论怎么操作都不可能使序列非减

如果 \(a_{n-1}\leq a_n\)

  • \(a_n\geq 0\) 时我们可以遵循一个简单的策略:\(i=n-2,n-3,\cdots 1\) 时依次执行操作 \((x,y,z)=(i,i+1,n)\) ,那么 \(a_i=a_{i+1}-a_n\leq a_{i+1}\)
  • \(a_n<0\)\(a_{n-2}\) 只有在 \(a_{n-2}\leq a_{n-1}\) 才可保证序列非减,否则 \(a_{n-2}=a_{n-1}-a_n>a_{n-1}\) ,同理可推 \(a_{n-3},a_{n-4},\cdots a_1\) ,所以只有原序列满足非减时才能进行 \(0\) 次操作,否则无论如何操作都不可能使序列非减
#include<bits/stdc++.h>
using namespace std;

const int MAX_N = 200000 + 5;
long long a[MAX_N];
int n;

struct node {
    int x, y, z;
} step[MAX_N];

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int t;
    cin >> t;
    while(t--) {
        cin >> n;
        for(int i = 1; i <= n; i++)
            cin >> a[i];
        if(a[n - 1] > a[n]) {
            cout << -1 << endl;
        } else {
            int cnt = 0;
            bool fnd = true;
            for(int i = n - 2; i >= 1; i--) {
                if(a[i] > a[i + 1]) {
                    if(a[n] >= 0) {
                        a[i] = a[i + 1] - a[n];
                        step[++cnt] = {i, i + 1, n};
                    } else {
                        fnd = false;
                        break;
                    }
                }
            }
            if(fnd) {
                cout << cnt << endl;
                for(int i = 1; i <= cnt; i++) 
                   cout << step[i].x << ' ' << step[i].y << ' ' << step[i].z << endl;
            } else {
                cout << -1 << endl;
            }
        }
    }
    return 0;
}

CF1634C.OKEA

题目链接CF1634C

题意

给定两个整数 \(n,k\) 构造一个 \(n\times k\) 的矩阵 \(a\) ,满足:

  • \(1\)\(n\cdot k\) 的每个整数都出现在矩阵上且仅出现一次
  • 对于任意 \(i,l,r\) 满足 \(a_{i,l},a_{i,l+1},\cdots,a_{i,r}\) 的算术平均值是整数

分析

可以发现任意一行内的奇偶性是相同的,如果一行内既存在奇数又存在偶数,那么一定存在一个奇偶相邻的情况,它们的算术平均值不是整数

若每行的奇偶性都相同,那么可以构造:

\[\left[\begin{array}{ccc} 1 & 3 & \cdots &2k-1\\ 2 & 4 & \cdots & 2k\\2k+1 & 2k+3 & \cdots & 4k-1\\ \vdots & \vdots & \vdots &\vdots\end{array}\right] \]

对任意一段 \(i,l,r\) 进行求和,可以用等差数列求和公式,可以发现和一定是 \(l+r-1\) 的倍数,那么算术平均值也一定是整数。进一步,可以求出通项公式 \(a_{i,j}=2(j+k\lfloor\frac{i-1}{2}\rfloor)-i\bmod 2\)

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

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int t;
    cin >> t;
    while(t--) {
        int n, k;
        cin >> n >> k;
        if((n % 2 && k == 1) || n % 2 == 0) {
            cout << "YES" << endl;
            for(int i = 1; i <= n; i++) {
                for(int j = 1; j <= k; j++)
                    cout << 2 * ((i - 1) / 2 * k + j) - i % 2 << ' ';
                cout << endl;
            }
        } else {
            cout << "NO" << endl;
        }
    }
    return 0;
}

CF1562C.Rings

题目链接:CF1562C

题意

给定一个长度为 \(n\) 的字符串,用 \(f(l,r)\) 表示子串 \(s_ls_{l+1}\cdots s_{r-1}s_r\) 代表的二进制数,判断是否存在 \((l_1,r_1),(l_2,r_2)\) 使得 \(\exist k\in\mathbb{N},f(l_1,r_1)=kf(l_2,r_2)\)

\(l_1,r_1,l_2,r_2\) 需要满足 \(r_1-l_1+1\geq \lfloor\frac{n}{2}\rfloor,r_2-l_2+1\geq\lfloor\frac{n}{2}\rfloor\)

分析

如果存在 \(i\geq \lfloor\frac{n}{2}\rfloor+1,s_i=0\) ,那么 \(f(1,i)=2f(1,i-1)\) 满足条件

否则 \(\forall i\geq \lfloor\frac{n}{2}\rfloor+1,s_i=1\) ,此时若 \(s_{\lfloor\frac{n}{2}\rfloor}=1\) ,可以取 \(f(\lfloor\frac{n}{2}\rfloor,2\lfloor\frac{n}{2}\rfloor-1)=f(\lfloor\frac{n}{2}\rfloor+1,2\lfloor\frac{n}{2}\rfloor)\) ,若 \(s_{\lfloor\frac{n}{2}\rfloor}=0\) ,可以取 \(f(\lfloor\frac{n}{2}\rfloor,2\lfloor\frac{n}{2}\rfloor)=f(\lfloor\frac{n}{2}\rfloor+1,2\lfloor\frac{n}{2}\rfloor)\)

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

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int t;
    cin >> t;
    while(t--) {
        int n;
        string s;
        cin >> n >> s;
        s = ' ' + s;
        bool fnd = false;
        for(int i = n / 2 + 1; i <= n; i++) {
            if(s[i] == '0') {
                cout << 1 << ' ' << i << ' ' << 1 << ' ' << i - 1 << endl;
                fnd = true;
                break;
            }
        }
        if(!fnd) {
            int m = n / 2;
            cout << m << ' ' << 2 * m - (s[m] == '1') << ' ' << m + 1 << ' ' << 2 * m << endl;
        }
    }
}

CF1614B.Divan and a New Project

题目链接CF1614B

题意

数轴上有 \(n+1\) 个整点 \(x_0,x_1,\cdots ,x_n\) ,此时你位于 \(x_0\) 。给定 \(a_1,a_2,\cdots,a_n\) ,求:

\[2\min\{\sum_{i=1}^n a_i\cdot |x_0-x_i|\} \]

分析

不妨设 \(x_0=0\) ,有一个显然的贪心策略:对 \(a_1,a_2,\cdots,a_n\) 从大到小排序,对于较大的 \(a_i\) 我们使 \(x_i\) 的绝对值尽量小,这样对答案的贡献也会更小

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

struct node {
    int val, idx;
} a[200000 + 5];
int x[200000 + 5];

bool cmp(node n1, node n2)
{
    return n1.val > n2.val;
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    int t;
    cin >> t;
    while(t--) {
        int n;
        long long sum = 0;
        cin >> n;
        for(int i = 1; i <= n; i++) {
            a[i].idx = i;
            cin >> a[i].val;
        }
        sort(a + 1, a + n + 1, cmp);
        for(int i = 1; i <= n; i++) {
            x[a[i].idx] = (i & 1 ? (i + 1) >> 1 : -(i >> 1));
            sum += 1ll * abs(x[a[i].idx]) * a[i].val;
        }
        cout << 2ll * sum << endl;
        x[0] = 0;
        for(int i = 0; i <= n; i++)
            cout << x[i] << ' ';
        cout << endl;
    }
    return 0;
}
posted @ 2022-03-27 21:27  f(k(t))  阅读(75)  评论(0编辑  收藏  举报