1.13 构造
1512D - Corrupted Array
题意
给出序列b,含有n+2个元素,其中第n+1个元素是前n个元素的和,第n+2个元素为任意数字,序列b内元素的顺序可能是紊乱的,如果有满足条件的序列,将前n个元素输出,否则输出-1
思路
给出了前n个元素的和,就代表可能要排序。将n+2个元素排序。因为有一个元素为前n个元素的和,所以它一定是最大的或者第二大的。判断即可
void solve() {
int n, m;
cin >> n;
m = n + 2;
vector<int> a(m + 1);
for (int i = 1; i <= m; i ++) {
cin >> a[i];
}
sort(a.begin() + 1, a.end());
LL sum = 0;
for (int i = 1; i <= n; i ++) {
sum += a[i];
}
if (sum > a[m]) {
cout << -1 << endl;
return ;
} else if (sum == a[m] || sum == a[m - 1]) {
for (int i = 1; i <= n; i ++) {
cout << a[i] << " \n"[i == n];
}
} else if (sum < a[m]) {
sum = sum + a[m - 1] - a[m];
int flag = -1;
for (int i = 1; i <= n; i ++) {
if (a[i] == sum) {
flag = i;
break;
}
}
if (flag == -1) {
cout << -1 << endl;
return ;
}
for (int i = 1; i <= n + 1; i ++) {
if (i == flag) continue;
cout << a[i] << " \n"[i == n + 1];
}
}
}
1512C - A-B Palindrome
题意
给出a,b,和一个只含有‘1’,‘0’,‘?’的字符串,要求构造出一个只含有a个‘0’,b个‘1’的回文字符串,通过将‘?’改成‘0’或者‘1’。问是否可以
思路
正常模拟即可。既然是回文字符串,首先将题目中给出的字符串整理一遍,将当前的s[i]和s[n - i + 1]进行比较。然后统计字符串中01数量是否超限,在判断字符串中是否存在成对儿的‘?’或者在中间的单个‘?’,若有则看01哪个有多余的,改成它即可。否则判负。最后将字符串倒序和正序比较,是回文字符串且a,b恰好用完即为所求,否则判负
void solve() {
int x, y;
cin >> x >> y;
string s;
cin >> s;
s = " " + s;
int n = x + y;
for (int i = 1; i <= n; i ++) {
if (s[i] != '?') { //如果当前字符有值
if (s[n - i + 1] != '?' && s[i] != s[n - i + 1]) { //如果对面字符有值且不和当前字符相等,-1
cout << -1 << endl;
return ;
} else if (s[n - i + 1] == '?'){ //如果对面字符无值,那么赋成和当前字符相等的值
s[n - i + 1] = s[i];
}
} else {
if (s[n - i + 1] != '?') { //如果当前字符无值 且对面有值
s[i] = s[n - i + 1];
}
}
}
for (int i = 1; i <= n; i ++) { //检查一下还剩多少01
if (s[i] == '0') x --;
if (s[i] == '1') y --;
}
for (int i = 1; i <= n; i ++) {
if (s[i] == '?') {
if (i != n - i + 1) { //若有成对儿的问号
if (x >= 2) {
x -= 2;
s[i] = s[n - i + 1] = '0';
} else if (y >= 2) {
y -= 2;
s[i] = s[n - i + 1] = '1';
}else {
cout << -1 << endl;
return ;
}
}else { //若是单个问号
if (x == 1) {
x --;
s[i] = '0';
}else if (y == 1) {
y --;
s[i] = '1';
}else {
cout << -1 << endl;
return ;
}
}
}
}
string t = s.substr(1);
string h = t;
reverse(t.begin(), t.end());
bool ok = 0;
if (t == h) ok = 1;
if (ok && !x && !y) {
cout << h << endl;
} else cout << -1 << endl;
}
1497C1 - k-LCM (easy version)
题意
给出数字n和k个数字,要求这k个数字之和为n且他们的最小公倍数小于等于n/2。输出这k个数字
思路
这个版本的k只有3,所以直接找规律即可。首先可以被三整除的一定可以分为3份且满足题意,不能被三整除的,但是是奇数,则可以由1和两个偶数组成,也是满足题意的
剩下只有偶数部分,偶数也分为两类,一种是8,16,32这类可以被四整除的,那么直接分为1/2,1/4,1/4即可。否则就将偶数减二再对半分也一定是满足题意的。
void solve() {
int n, k;
cin >> n >> k;
if (n % 3 == 0) {
for (int i = 1; i <= 3; i ++) cout << n / 3 << " \n"[i == 3];
}else if (n % 2 == 1) {
n --;
cout << n / 2 << ' ' << n / 2 << ' ' << 1 << endl;
}else {
int t = n - 2;
if (t % 4 == 0) {
cout << t / 2 << ' ' << t / 2 << ' ' << 2 << endl;
}else {
cout << n / 2 << ' ' << n / 4 << ' ' << n / 4 << endl;
}
}
}
1521B - Nastia and a Good Array
题意
给出n个数字,可以对它进行若干操作,然后要求使这组数字相邻之间的gcd为1。操作为:选择任意下标i, j将a[i], a[j]改成x, y。且min(a[i], a[j]) == min(x, y)
思路
题目限制操作次数不大于n,那么我们可以进行n次操作。因为我们知道连续的两个数一定gcd为1,如gcd(2,3) = 1, gcd(23, 24) = 1。那么就想如何将这组数构造成这种连续的数字。
那么首先找到数组中最小的数,然后将它换到开头,然后对后面n-1个数字进行更改,每个都在前一个数字的基础上加一即可。
如:9 6 3 11 15
首先:3 6 9 11 15
然后:
3 4 9 11 15
3 4 5 11 15
3 4 5 6 15
3 4 5 6 7
结束。正好n次操作
struct node{
int i, j, x, y;
};
//注意观察N的大小
void solve() {
int n;
cin >> n;
vector<int> a(n + 1);
int mnv = INF, flag = -1;
for (int i = 1; i <= n; i ++) {
cin >> a[i];
if (mnv > a[i]) {
mnv = a[i];
flag = i;
}
}
vector<node> v;
if (flag != 1)
v.push_back({1, flag, mnv, a[1]});
int x = mnv;
for (int i = 2; i <= n; i ++) {
v.push_back({1, i, mnv, ++ x});
}
cout << v.size() << endl;
for (auto q : v) {
cout << q.i << ' ' << q.j << ' ' << q.x << ' ' << q.y << endl;
}
}
1453B - Suffix Operations
题意
给出序列a,有两种操作:
1.后缀-1
2.后缀+1
你需要让a的所有元素全部相等,同时你可以改变一个数字以减少操作次数
问最少操作数是多少
思路
这种题应该一眼差分,但是没想出来。看的题解,枚举每个元素,找出一个波峰或者波谷,让拐点等于两端中的任意一段即可最大程度降低操作次数,其中对于第一个元素和最后一个元素只需要跟相邻元素比较即可
void solve() {
int n;
cin >> n;
vector<LL> a(n + 1);
for (int i = 1; i <= n; i ++) {
cin >> a[i];
}
LL sum = 0, mx = -INF;
for (int i = 2; i <= n; i ++) {
sum += abs(a[i - 1] - a[i]);
}
mx = max(mx, abs(a[1] - a[2]));
mx = max(mx, abs(a[n - 1] - a[n]));
for (int i = 2; i < n; i ++) {
mx = max(mx, abs(a[i - 1] - a[i]) + abs(a[i + 1] - a[i]) - abs(a[i - 1] - a[i + 1]));
}
cout << sum - mx << endl;
}
1421C - Palindromifier
题意
给出一个字符串S,你有两种操作:
1.从下标i一直到s[2],将其倒转加在字符串前面
2.从下标i一直到s[n - 1],将其倒转加在字符串后面
问是否可以通过这两种操作,不超过30次将字符串变成回文字符串
思路
这题很巧妙,直接观察多试几次就可以知道规律
先找到倒数第二个字符,然后用操作2,将这个字符放在字符串后面,然后在更新后的字符串的倒数第二个字符,也就是原来字符的最后一个进行操作1,然后字符串里回文字符串就只有最后一个字符的差异,而此时会神奇的发现字符串的第二个字符就是那个字符,在用一次操作1即可
模拟一遍:
原字符串:xyzq
1.xyzqz
2.qzyxyzqz
3.zqzyxyzqz
最后我在前面判了是不是一开始就是回文串,但其实无所谓
void solve() {
string s, t;
cin >> s;
t = s;
reverse(t.begin(), t.end());
if (s == t) {
cout << 0 << endl;
return ;
}
int n = s.size();
cout << 3 << endl;
cout << "R " << n - 1 << endl;
cout << "L " << n << endl;
cout << "L " << 2 << endl;
}

浙公网安备 33010602011771号