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(贪心)

题目链接

题目:
给出一叠牌,两个空位(一个有牌,一个没牌),每次可以取出一部分牌按顺序放在另一堆上,如果要保证\(排序值\)最大,则要如何移动牌堆
排序值定义

\[\sum_{i=1}^nn^{n−i}\times p_i. \]

解析:
由排序值定义可知,越大的数排在越前面这个值越大,则每次寻找出剩余序列中最大的数,将此后的数放入另一堆中,即可构成此序列

#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
所以这对二进制数一定是以下述方式存在的

\[1\dots1|1|1\dots1|0\dots0|0|0\dots0\\ 1\dots1|0|1\dots1|0\dots0|1|0\dots0 \]

#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\)挑选两处不同点进行更改,判断可行就输出

注意:

  1. 在不同点等于3时第一个数组需要更改的地方不一定只有一处
  2. 要保存好每次进行判断检测时最大值所在数组编号的副本,防止后序检测后被更替
#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");
}
posted @ 2021-02-23 21:35  DreamW1ngs  阅读(87)  评论(0编辑  收藏  举报