南昌理工acm暑假集训
本周主要参加多校赛训练和做acwing的每日一题
就写一些我认为还不错的题的题解吧
**
第一道题是
acwing数组补全
题目来源于CF1283C
这道题主要涉及到 构造 和 环图
主要做题思路是模拟
代码思路:
1.就是硬模拟
2.找出缺失的数字
3.找出缺失的位置
4.找出缺失的数字,同时也是缺失位置的情况----“重复的数字”
5.找出缺失的数字,不是缺失位置的情况-------“非重复的数字”
6.分情况讨论
(1)如果重复的数字超过了两个。这些超过的数字中,后一个填前一个的坑就可以。
(2)如果重复的数字只有一个,这个重复的位置需要用非重复的数字去填。
(3)如果重复的数字有0个。非重复的数字,直接挨着往缺失的位置填即可。
题解:(自己没码成功,这是参考过y总的代码写的)
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 200010;
int n;
int p[N], q[N];
bool st[N];
int main()
{
int T;
scanf("%d", &T);
while (T -- )
{
scanf("%d", &n);
memset(q, 0, sizeof q);
memset(st, 0, sizeof st);
for (int i = 1; i <= n; i ++ )
{
scanf("%d", &p[i]);
q[p[i]] = i;
}
bool flag = false;
for (int i = 1; i <= n; i ++ )
{
if (st[i] || !p[i]) continue;
st[i] = true;
int x = i, y = i;
while (p[x] && !st[p[x]])
{
x = p[x];
st[x] = true;
}
while (q[y] && !st[q[y]])
{
y = q[y];
st[y] = true;
}
if (p[x] == y) continue;
if (!flag)
{
flag = true;
for (int j = 1; j <= n; j ++ )
if (!p[j] && !q[j])
{
st[j] = true;
p[x] = j;
x = j;
}
}
p[x] = y;
}
if (!flag)
{
int x = 0, y = 0;
for (int i = 1; i <= n; i ++ )
if (!p[i])
{
if (!x && !y) x = y = i;
else
{
p[x] = i;
x = i;
}
}
p[x] = y;
}
for (int i = 1; i <= n; i ++ )
printf("%d ", p[i]);
puts("");
}
return 0;
}
第二道题是
acwing交换相邻元素
题目来源:CF920C
这是一道思维题
主要思路为排序
代码思路:
1.我们假设 [l,r] 区间内字符均为 1,则说明 [l,r+1]区间内的元素可以随意互换,而不能与该区间以外的元素交换。为了最终使其成为升序数组,我们可以将该段内数组元素升序排序。
2.遍历 01 字符串,双指针找到连续的一段可交换的区间,将该区间内元素升序排序。遍历完整个字符串后,如果数组 a 是从 1 到 n 的升序全排列,则输出 YES,否则输出 NO
3.最终可以用 is_sorted(a.begin(), a.end()) 可以直接判断是否升序!
题解:
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
vector<int> a(n);
string b;
for(int i = 0; i < n; i++) cin >> a[i];
cin >> b;
// 双指针找连续可交换区间
int l = 0, r = 0;
while(r < n) {
while(b[r] == '1') r++;
sort(a.begin() + l, a.begin() + r + 1);
r++;
l = r;
}
// 判断最终数组是否为升序
bool ok = true;
for(int i = 0; i < n; i++) {
if(a[i] != i + 1) {
ok = false;
break;
}
}
if(ok) cout << "YES\n";
else cout << "NO\n";
return 0;
}
代码思路和题解参考acwing大佬GTAlgorithm
第三题是
acwing三元数异或
题目来源CF1328C
思路
根据结果 x 的每一位上的数字 xixi 进行分类讨论如下(假设 a⩾b):
- 当 xi=0 时,a 和 b 对应位置上的数字可以为 (0, 0) 或者 (1, 2),因为要求 a 尽可能小,所以 ai=0,bi=0。
- 当 xi=1 时,a 和 b 对应位置上的数字可以为 (0, 1) 或者 (2, 2),因为要求 a 尽可能小且 a⩾b,所以在 a 和 b 还没有确定大小关系时,应该是 ai=1,bi=0;当 a 和 b 已经确定大小时,为了使 a 尽可能小,应该是 ai=0,bi=1。
- 当 xi=2 时,a 和 b 对应位置上的数字可以为 (0, 2) 或者 (1, 1),因为要求 a 尽可能小且 a⩾b,所以在 a 和 b 还没有确定大小关系时,应该是 ai=1,bi=1;当 a 和 b 已经确定大小时,为了使 a 尽可能小,应该是 ai=0,bi=2。
使用 flag 记录 a 和 b 是否已经确定大小关系,根据上述讨论可知,在 a 和 b 没有确定大小关系时,遇到 xi=0和 xi=2,ai 和 bi 会填上相同的数字,直到遇到 xi=1 有 ai=1,bi=0,a 和 b 可以确定大小关系。
题解:
#include<bits/stdc++.h>
using namespace std;
char c[51000],a[51000],b[51000];
int t,n;
int main()
{
cin>>t;
while(t--)
{
int f=0;
cin>>n;
cin>>c;
for(int i=0;i<n;i++)
{
if(c[i]=='2'&&f==0) a[i]=b[i]='1';
else if(c[i]=='1'&&f==0)
{
a[i]='1',b[i]='0';
f=1;
}
else if(c[i]=='0') a[i]=b[i]='0';
else if(f==1) a[i]='0',b[i]=c[i];
}
for(int i=0;i<n;i++)
cout<<a[i];
cout<<endl;
for(int i=0;i<n;i++)
cout<<b[i];
cout<<endl;
}
}