Codeforces Round #704 (Div. 2)
A(水题)
题目链接
⭐
题目:
给出\(a\),\(b\),\(c\),求出比\(c\)大的最近的\(a\)或\(b\)的倍数到\(c\)的距离
解析:
向上找最近倍数取最小值即可
#include<bits/stdc++.h>
using namespace std;
/*===========================================*/
int main() {
int T;
LL p, a, b, c;
scanf("%d", &T);
while (T--) {
scanf("%lld%lld%lld%lld", &p, &a, &b, &c);
LL t[3] = { (p + a - 1) / a * a,(p + b - 1) / b * b ,(p + c - 1) / c * c };
printf("%lld\n", min({ t[0],t[1],t[2] }) - p);
}
}
B(贪心)
题目链接
⭐
题目:
给出一叠牌,两个空位(一个有牌,一个没牌),每次可以取出一部分牌按顺序放在另一堆上,如果要保证\(排序值\)最大,则要如何移动牌堆
排序值定义
解析:
由排序值定义可知,越大的数排在越前面这个值越大,则每次寻找出剩余序列中最大的数,将此后的数放入另一堆中,即可构成此序列
#include<bits/stdc++.h>
using namespace std;
/*===========================================*/
const int maxn = 1e5 + 5;
int dat[maxn];
bool vis[maxn];
int main() {
int T, n;
scanf("%d", &T);
while (T--) {
MEM(vis, 0);
scanf("%d", &n);
for (int i = 0; i < n; ++i)
scanf("%d", &dat[i]);
int mx = n;
int last = n;
for (int i = n - 1; i >= 0; --i) {
vis[dat[i]] = true;
if (dat[i] == mx) {
for (int j = i; j < last; ++j)
printf("%d ", dat[j]);
while (vis[mx])
--mx;
last = i;
}
}
printf("\n");
}
}
C(思维)
题目链接
⭐⭐
题目:
给出字符串\(s,t\),长度分别为\(n\),\(m\),必定存在\(p\)序列,满足\(1\le p_1<p_2<\dots<p_m\le n\),并且\(s_{p_i}=t_i\),求出拥有最大相邻元素间距的\(p\)序列
解析:
对于任意一个\(t\)中的字符可以映射到\(s\)中的一些字符,求出每个字符能够映射的最小合法距离和最大合法距离,求出\(\max_{i=2}^m(high[i]-[low[i-1]])\)
#include<bits/stdc++.h>
using namespace std;
/*===========================================*/
const int maxn = 2e5 + 5;
char s[maxn], t[maxn];
int lens, lent;
int low[maxn], high[maxn];
int main() {
scanf("%d%d", &lens, &lent);
scanf("%s%s", s + 1, t + 1);
int p = 1;
for (int i = 1; i <= lent; ++i, ++p) {
if (s[p] != t[i]) ++p;
low[i] = p;
}
p = lens;
for (int i = lent; i > 0; --i, --p) {
if (s[p] != t[i]) --p;
high[i] = p;
}
int mx = 0;
for (int i = 2; i <= lent; ++i)
mx = max(mx, high[i] - low[i - 1]);
printf("%d", mx);
}
D(思维)
题目链接
⭐⭐⭐
题目:
存在两个二进制数,均有\(a\)个\(0\)和\(b\)个\(1\),且大的二进制数减去小的二进制数有\(k\)个\(1\),若这样的一对二进制数存在,求出对应的二进制数
解析:
假设下列讨论01代表同一位大数为0,小数为1;00代表同一位大数为0,小数为0...
对于00,11,10情况,在未出现借位时,贡献分别为0,0 ,1
对于01时 贡献为1,出现借位,对后序出现的4种情况进行讨论
- 00 贡献为1 继续借位
- 01 贡献为0 继续借位
- 11 贡献为1 继续借位
- 10 贡献为0 停止借位
可以看出每对01都需要一对10来终止,且可以肯定的是01与10出现的次数相等,且花费了2个0和两个1只能贡献1,而如果这两个0两个1以00,11的形式出现在借位过程中可以获得更多个1
所以这对二进制数一定是以下述方式存在的
#include<bits/stdc++.h>
using std::min;
/*===========================================*/
const int maxn = 2e5 + 5;
char ret[2][maxn];
int index = 0;
int main() {
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
int t = min(a, b);
if (!c || c <= a + b - 2 && !a && b != 1)
{
if (c)
{
--a, --b, --c;
while (a + b > c && b--)
ret[0][index] = ret[1][index] = '1', ++index;
ret[0][index] = '1';
ret[1][index++] = '0';
while (a-- && c--)
ret[0][index] = ret[1][index] = '0', ++index;
while (c-- > 0)
ret[0][index] = ret[1][index] = '1', ++index;
ret[0][index] = '0';
ret[1][index++] = '1';
while (~(a--))
ret[0][index] = ret[1][index] = '0', ++index;
}
else
{
for (int i = 0; i < b; ++i, ++index)
ret[0][index] = ret[1][index] = '1';
for (int i = 0; i < a; ++i, ++index)
ret[0][index] = ret[1][index] = '0';
}
printf("%s\n%s", ret[0], ret[1]);
}
else
printf("No");
}
E(思维+搜索)
题目链接
⭐⭐⭐⭐
题目:
给出\(n\)组关于某一个长度为\(m\)的数组的复制,每组种至多只有两处与原数组不同,判断这样的原数组是否存在并输出
解析:
可以假定第一个数组就是原数组,朴素的去统计每组数据最多的不同之处,进行分类讨论
可以分析第一个数组存在至多两处不同,其他数组也至多存在两个不同,所以第一个数组为假定的情况下与其他数组至多出现4处不同
总结:
- 如果最多不同点小于等于2,则第一个复制数组即可作为原数组
- 如果不同点等于3,则根据不同点为3的复制数组进行更改,使得这个数组的不同点降为2
- 再次进行所有复制品的判断,如果不同点小于等于2,则输出当前更改后的数组
- 否则则再次根据不同点为3的复制数组进行更改,进行所有复制品的判断
- 只有不同点小于等于2时才输出
- 否则则失败
- 如果不同点等于4,则\(C_4^2\)挑选两处不同点进行更改,判断可行就输出
注意:
- 在不同点等于3时第一个数组需要更改的地方不一定只有一处
- 要保存好每次进行判断检测时最大值所在数组编号的副本,防止后序检测后被更替
#include<bits/stdc++.h>
using namespace std;
/*===========================================*/
vector<vector<int> > dat;
int n, m;
vector<int> ret;
int dif;
int id;
void cnt() {
dif = 0;
for (int i = 1; i < n; ++i) {
int c = 0;
for (int j = 0; j < m; ++j)
if (ret[j] != dat[i][j])
++c;
if (c > dif) {
dif = c;
id = i;
}
}
}
void yes() {
printf("Yes\n");
for_each(ret.begin(), ret.end(), [](int& i) {printf("%d ", i); });
exit(0);
}
int main() {
scanf("%d%d", &n, &m);
dat.assign(n, vector<int>(m));
for (int i = 0; i < n; ++i)
for (int j = 0; j < m; ++j)
scanf("%d", &dat[i][j]);
ret.assign(dat[0].begin(), dat[0].end());
cnt();
if (dif <= 2) yes();
else if (dif == 3) {
int tmp = id;
for (int i = 0; i < m; ++i) {
if (dat[tmp][i] != ret[i]) {
ret[i] = dat[tmp][i];
cnt();
int iid = id;
if (dif <= 2) yes();
else if (dif == 3) {
for (int j = 0; j < m; ++j)
if (dat[iid][j] != ret[j]) {
int t = ret[j];
ret[j] = dat[iid][j];
cnt();
if (dif <= 2)
yes();
ret[j] = t;
}
}
ret[i] = dat[0][i];
}
}
}
else if (dif == 4) {
int iid = id;
int r[4], p = 0;
for (int i = 0; i < m; ++i)
if (dat[id][i] != ret[i])
r[p++] = i;
for (int i = 1; i < 4; ++i)
for (int j = 0; j < i; ++j) {
ret[r[i]] = dat[iid][r[i]];
ret[r[j]] = dat[iid][r[j]];
cnt();
if (dif <= 2) yes();
ret[r[i]] = dat[0][r[i]];
ret[r[j]] = dat[0][r[j]];
}
}
printf("No");
}