构造题合集
简介
构造题是一种有趣的题目类型,一般会给定几个条件,要求构造出符合这些条件的数字/序列等,与数学关联较大
题目
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}\) 的算术平均值是整数
分析:
可以发现任意一行内的奇偶性是相同的,如果一行内既存在奇数又存在偶数,那么一定存在一个奇偶相邻的情况,它们的算术平均值不是整数
若每行的奇偶性都相同,那么可以构造:
对任意一段 \(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\) ,求:
分析:
不妨设 \(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;
}