首页 > 技术文章 > Educational Codeforces Round 107 (Rated for Div. 2)

DreamW1ngs 2021-04-13 20:42 原文

比赛地址

A(水题)

题目链接

题目:
有两个计票员,\(n\)个观众,每个观众可以按顺序投票,如下所示有三种票

  • 1 代表支持
  • 2 代表不支持
  • 3 如果当前计票员收到的支持票数大于等于不支持票数,则投支持,反之不支持
    求出支持票数的最大值

解析:
让一个计票员专门收反对票,另一个计票员收其他种类的票,很显然答案就是\(1\)\(3\)的票数之和

#include<bits/stdc++.h>
using namespace std;
/*===========================================*/
 
int main() {
	//FRE;
	int T, n, t;
	scanf("%d", &T);
	while (T--) {
		scanf("%d", &n);
		int t1 = 0, t2 = 0;
		while (n--) {
			scanf("%d", &t);
			if (t & 1) ++t1;
			else ++t2;
		}
		printf("%d\n", t1);
	}
}

B(GCD)

题目链接

题目:
给出\(a,b,c\)代表\(A,B,C\)三个数所代表的十进制位数,同时满足\(GCD(A,B)=C\),输出满足条件的\(A,B\)

解析:
由于\(A=A'C,B=B'C\)\(A'\)\(B'\)互质,考虑C为10的幂,可以很好的控制A与B的位数,那么任务就转变成了寻找位数分别为\(a-c+1\)\(b-c+1\)且互质的\(A'\)\(B'\),不难发现\(10^x与10^y+1\)一定互质(由于前者只能质因数分解成若干个2与5的幂次,而后者一定不能被2或5整除),那么问题迎刃而解

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
/*===========================================*/

ll ten[10];

int main() {
	//FRE;
	int T;
	ten[1] = 1;
	for (int i = 2; i < 10; ++i)
		ten[i] = ten[i - 1] * 10;
	scanf("%d", &T);
	int a, b, c;
	while (T--) {
		scanf("%d%d%d", &a, &b, &c);
		printf("%lld %lld\n", ten[a - c + 1] * ten[c], (ten[b - c + 1] + 1) * ten[c]);
	}
}

C(思维)

题目链接
⭐⭐

题目:
给出一叠牌,从上到下给出每个牌的颜色,有\(m\)次询问,每次询问最上方颜色为\(t\)的牌的位置,输出,并把这张牌抽出放于牌顶

解析:
由于每次每次询问最上方的牌,并把它放在牌顶,所以只需要记录每种颜色最上方的牌,并在每次查询时,维护好这个颜色队列即可

#include<bits/stdc++.h>
using namespace std;
/*===========================================*/

const int maxn = 3e5 + 5;

int q[maxn];
int cnt = 0;
bool vis[maxn];
int pos[maxn];

int main() {
	//FRE;
	int n, m, t;
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i) {
		scanf("%d", &t);
		if (!vis[t]) {
			vis[t] = true;
			q[cnt++] = t;
			pos[t] = i;
		}
	}
	while (m--) {
		scanf("%d", &t);
		printf("%d ", pos[t]);
		int i = 0;
		for (; q[i] != t; ++i)
			++pos[q[i]];
		for (; i > 0; --i)
			q[i] = q[i - 1];
		pos[t] = 1;
		q[0] = t;
	}
}

D(构造)

题目链接
⭐⭐

题目:
如果\(s_i=s_j(i<j)\),且\(s_{i+1}=s_{j+1}\),则花费加1,现在给出字符串长度,以及字符集大小,求出最小代价的字符串

解析:
可以尝试考虑重复构造代价为0的串,即时刻保证当前两位后缀从未在之前出现过,可以考虑这样的构造方法,每次从\(a\)开始枚举字符,先输出一个\(cur\),再输出\(cur\#\)\(cur\)代表当前字符,\(\#\)代表比\(cur\)大的字符,这样可以保证所有字符集合中所有两两组合都出现过

#include<bits/stdc++.h>
using namespace std;
/*===========================================*/

int n, m, c;

void P(int i) {
	printf("%c", i + 'a');
	if (++c == n) exit(0);
}

int main() {
	//FRE;
	scanf("%d%d", &n, &m);
	while (1) {
		for (int i = 0; i < m; ++i) {
			P(i);
			for (int j = i + 1; j < m; ++j)
				P(i), P(j);
		}
	}
}

E(dp+组合数学)

题目链接
⭐⭐⭐⭐

题目:
给出一个矩阵,\(o\)代表白块,\(*\)代表黑块,每个白块可以染成redblue两种颜色,横排两个连续的红块可以防止一个多米诺牌,竖排两个连续的蓝块可以放置一个多米诺牌,多米诺牌不能重叠,每个白块必须被染色,问每种染色方案所能放置的最大多米诺牌数之和(取模\(998244353\)

解析:

  1. 考虑对于某一行连续的白块,定义\(dp[i]\)为前\(i\)个连续白块最多能放置的多米诺牌之和,那么对于\(dp[i+1]\)

    • 如果第\(i+1\)个白块被染成蓝色,则此时剩余白块的贡献为\(dp[i]\)
    • 如果第\(i+1\)个白块被染成红色
      • 如果第\(i\)个白块被染成红色,则此时剩余\(i-1\)个白块的贡献为\(dp[i-1]\),最后两个红块对于整体的贡献为\(2^{i-1}\)
      • 如果第\(i\)个白块被染成蓝色,则此时剩余白块的贡献为\(dp[i-1]\)
  2. 综上可以得到状态转移方程\(dp[i]=dp[i-1]+2*dp[i-2]+2^{i-2}\)

  3. 那么统计所有连续的白块行和白块列,利用\(dp\)可以\(O(1)\)求出这段白块行/列对于每种染色矩阵的贡献,因此假设这段白块长度为\(x\),答案加上\(dp[x]*2^{white-x}\)

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
/*===========================================*/
using namespace std;
const int maxn = 3e5 + 5, mod = 998244353.;
ll dp[maxn], two[maxn];
string e[maxn];

int main() {
	IOS
	two[0] = 1;
	int n, m;
	for (int i = 1; i < maxn; ++i)
		two[i] = two[i - 1] * 2 % mod;
	for (int i = 2; i < maxn; ++i)
		dp[i] = (dp[i - 1] + 2 * dp[i - 2] % mod + two[i - 2]) % mod;
	cin >> n >> m;
	int end = max(n, m);
	ll ret = 0, c;
	int white = 0;
	for (int i = 0; i < n; ++i) {
		cin >> e[i];
		for (auto& j : e[i])
			white += j == 'o';
	}
	for (int i = 0; i < n; ++i) {
		c = 0;
		for (int j = 0; j < m; ++j) {
			if (e[i][j] == 'o') ++c;
			else if (c) {
				ret = (ret + dp[c] * two[white - c]) % mod;
				c = 0;
			}
		}
		if (c)
			ret = (ret + dp[c] * two[white - c]) % mod;
	}
	for (int j = 0; j < m; ++j) {
		c = 0;
		for (int i = 0; i < n; ++i) {
			if (e[i][j] == 'o') ++c;
			else if (c) {
				ret = (ret + dp[c] * two[white - c]) % mod;
				c = 0;
			}
		}
		if (c)
			ret = (ret + dp[c] * two[white - c]) % mod;
	}
	cout << ret;
}

推荐阅读