構造題合集
簡介
構造題是一種有趣的題目型別,一般會給定幾個條件,要求構造出符合這些條件的數字/序列等,與數學關聯較大
題目
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;
}