1. 程式人生 > 其它 >構造題合集

構造題合集

簡介

構造題是一種有趣的題目型別,一般會給定幾個條件,要求構造出符合這些條件的數字/序列等,與數學關聯較大

題目

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)\)

Build the Permutation#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;
}