Educational Codeforces Round 113 (Rated for Div. 2)

本文介绍了三个编程问题:BalancedSubstring解决字符串中字符等频子串查找;ChessTournament探讨满足多人期望的比赛结果设计;JuryMeeting计算良好发言顺序数量。展示了如何通过暴力搜索、逻辑分析和数学计数解决问题。
摘要由CSDN通过智能技术生成

A. Balanced Substring

题意:
字符串 s 由字符 a 和 b 构成,寻找 s 的子串 s[l;r],使得子串中字符 a 和 b 的数量相同,输出 l, r

思路:
范围很小,暴力解决
求出 s 子串 s[0; i] 中字符 a 和 b 的数量为 a[i] 和 b[i],枚举 s 的子串 l,r
判断 l,r 内字符 a 和 b 的数量是否相同
字符 a 和 b 数量的求法:a[r] - a[l - 1]

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
using namespace std;
#pragma warning(disable:4996)

const int N = 55;
int a[N], b[N];

int main()
{
	int T, n;
	scanf("%d", &T);
	string s;
	while (T--)
	{
		scanf("%d", &n);
		cin >> s;
		a[0] = (s[0] == 'a');
		b[0] = (s[0] == 'b');
		for (int i = 1; i < n; ++i) {
			a[i] = a[i - 1] + (s[i] == 'a');
			b[i] = b[i - 1] + (s[i] == 'b');
			// cout << a[i] << " " << b[i] << endl;
		}
		bool ok = false;
		for (int i = 1; i < n; ++i) {
			for (int j = 0; j < i; ++j) {
				if (j == 0 && a[i] == b[i]) {
					printf("%d %d\n", j + 1, i + 1);
					ok = true;
					break;
				}
				else if (a[i] - a[j - 1] == b[i] - b[j - 1]) {
					printf("%d %d\n", j + 1, i + 1);
					ok = true;
					break;
				}
			}
			if (ok) break;
		}
		if (!ok) printf("-1 -1\n");
	}
	system("pause");
	return 0;
}

B. Chess Tournament

题意:
n 个人下棋,每个人都有自己的期望,一共有两种期望:

  1. 一局都不输
  2. 至少赢依据
    每局有三种结果:赢(+),输(-),平局(=)
    现给出每个人的期望,判断是否存在一种比赛结果,满足所有人的要求

思路:
对于一个 n * n 大小的结果矩阵,对角线都是 X,且为对称矩阵。
所以可供更改的位置只有: ( n ∗ n − n ) / 2 = n ∗ ( n − 1 ) / 2 (n * n - n)/2 = n*(n-1)/2 (nnn)/2=n(n1)/2

如果一个人的期望是第一种情况,那么他所在的那一行都是平局,于是确定了 n − 1 n - 1 n1 个位置
如果还有一个人也期望第一种,那么由于前一个人已经和他是平局,于是他确定了 n − 2 n - 2 n2 个位置

如果有 n u m 1 num1 num1 个人都期望第一种,那么一共确定了:
n − 1 + n − 2 + n − 3 + . . . + n − n u m 1 = n ∗ n u m 1 + n u m 1 ∗ ( n u m 1 + 1 ) / 2 n-1 + n-2 + n - 3 +...+ n - num1=n*num1 + num1 *(num1 + 1)/2 n1+n2+n3+...+nnum1=nnum1+num1(num1+1)/2
那么还剩下可以更改的位置数为:
n ∗ ( n − 1 ) / 2 − n ∗ n u m 1 + n u m 1 ∗ ( n u m 1 + 1 ) / 2 n*(n-1)/2 - n*num1 + num1 *(num1 + 1)/2 n(n1)/2nnum1+num1(num1+1)/2
如果期望为 2 的人数小于等于剩余可更改的位置,那么满足所有人要求的结局是存在的

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
using namespace std;
#pragma warning(disable:4996)

const int N = 55;
char g[N][N];

int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		int n;
		scanf("%d", &n);
		for (int i = 0; i < n; ++i)
			for (int j = 0; j < n; ++j)
				g[i][j] = '0';

		string s;
		cin >> s;
		int num1 = 0, num2 = 0;
		for (int i = 0; i < n; ++i) {
			if (s[i] == '1') {
				++num1;
				for (int j = 0; j < n; ++j) g[i][j] = g[j][i] = '=';
			}
			else ++num2;
			g[i][i] = 'X';
		}
		bool ok = false;
		if ((n * (n - 1)) / 2 - (n * num1 - num1 * (num1 + 1) / 2) >= num2)
		{
			printf("YES\n");
			ok = true;
		}
		else {
			printf("NO\n");
			continue;
		}
		for (int i = 0; i < n; ++i) {
			if (s[i] == '2') {
				for (int j = 0; j < n; ++j) {
					if (g[i][j] == '0') {
						g[i][j] = '+';
						g[j][i] = '-';
						break;
					}
				}
			}
		}
		for (int i = 0; i < n; ++i) {
			for (int j = 0; j < n; ++j) {
				if (g[i][j] == '0') g[i][j] = '=';
				cout << g[i][j];
			}
			cout << endl;
		}
	}
	system("pause");
	return 0;
}

C. Jury Meeting

题意:
n 个人开会讨论问题,每个人都有自己待提出的问题,第 i 个人的问题数用 a[i] 表示。
对每个人的发言顺序进行排列,每个人依次提出自己的问题,如果下一个人还有问题没有提出,就轮到下一个人,否则就忽略他,轮到下下一个人,依次类推。
如果一个人连续两次及以上都是他发言,那么这个发言顺序被认为是不好的。
求有多少个好的发言顺序?

思路:
如果说最大问题数比第二大的数大得超过1,那么无论怎么排列,最大数都会剩下2个及以上的问题,而其他人的问题已经都讨论完成,那么他必然要连续两次发言,这时答案是 0

如果说有两个及以上的最大数,那么最后就只剩下这些拥有最大数的人轮流讨论问题,这时答案是 A n n A_n^n Ann

如果说只有一个最大数,第二大的数也就是次大数的个数为 x x x ,如果要保证拥有最大数的那个人连续两次发言,必须使得至少一个次大数在最大数后面
x x x 个次大数全排列 A x x A_x^x Axx,形成了 x + 1 x+1 x+1个空,为了保证最大数不在末尾,在前 x x x 个空中选择一个为 C x 1 C_x^1 Cx1
然后形成了 x + 1 + 1 x + 1 + 1 x+1+1 个空,等待后面的数插入为 C x + 2 1 C_{x+2}^1 Cx+21
然后形成了 x + 1 + 2 x + 1 + 2 x+1+2 个空,等待后面的数插入为 C x + 3 1 C_{x+3}^1 Cx+31

然后形成了 n n n 个空,等待后面的数插入为 C n 1 C_n^1 Cn1
所以最终答案为: A x x ∗ C x 1 ∗ ( C x + 2 1 ∗ C x + 3 1 ∗ . . . ∗ C n 1 ) = x ! ∗ x ∗ [ ( x + 2 ) ∗ ( x + 3 ) ∗ . . . ∗ n ] A_x^x * C_x^1 *(C_{x+2}^1*C_{x+3}^1*...*C_n^1)=x!*x*[(x+2)*(x+3)*...*n] AxxCx1(Cx+21Cx+31...Cn1)=x!x[(x+2)(x+3)...n]

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
using namespace std;
#pragma warning(disable:4996)

typedef long long ll;
const int N = 2e5 + 5;
ll A[N];
int a[N];
ll mod = 998244353;
void init()
{
	A[0] = A[1] = 1;
	for (int i = 2; i <= 2e5; ++i) {
		A[i] = A[i - 1] * i % mod;
	}
}

int main()
{
	int T;
	scanf("%d", &T);
	init();
	while (T--)
	{
		int n, maxn = -1;
		scanf("%d", &n);
		for (int i = 0; i < n; ++i) {
			scanf("%d", &a[i]);
			maxn = max(maxn, a[i]);
		}
		int num = 0, maxnum = 0;
		for (int i = 0; i < n; ++i) {
			if (a[i] != maxn && a[i] + 1 == maxn)
				++num;
			if (a[i] == maxn) ++maxnum;
		}
		if (maxnum == n || maxnum > 1) printf("%lld\n", A[n]);
		else if (num == 0) printf("0\n");
		else {
			ll ans = num;
			for (int i = 1; i <= num; ++i)
				ans = (ans * i) % mod;
			for (int i = num + 2; i <= n; ++i)
				ans = (ans * i) % mod;
			printf("%lld\n", ans);
		}
	}
	system("pause");
	return 0;
}

D. Inconvenient Pairs

题意:
一个占地 ( 0 , 0 ) (0,0) (0,0) ( 1 0 6 , 1 0 6 ) (10^6, 10^6) (106,106) 的城市,有 n n n 个纵向街道和 m m m 个横向街道。街道上有 k k k 个人,每个人的位置为 ( x p , y p ) (x_p, y_p) (xp,yp)
如果一个人到另一个人的位置所走过的距离大于他们之间的曼哈顿距离,那么称其为不方便对
求不方便对的数量

思路:
横向和纵向的介绍将城市划分为很多的网格
对纵向街道来说,如果两个城市在两个相邻纵向街道之间的横向街道上,那么他们是一个不方便对
同理对横向街道也是这样
只要求出每两个相邻街道所夹的每一条异向街道上人的数量,然后两两组合求出数量

代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <string>
#include <map>
using namespace std;
#pragma warning(disable:4996)
typedef long long ll;
typedef pair<int, int> PII;

const int N = 1e5 + 1;
int X[N << 1], Y[N << 1];
PII p[N * 3];
map<int, int> lcnt, rcnt;

bool mycmpl(PII a, PII b)
{
	return (a.first == b.first) ? (a.second < b.second) : a.first < b.first;
}

bool mycmpr(PII a, PII b)
{
	return (a.second == b.second) ? (a.first < b.first) : a.second < b.second;
}

int main()
{
	int T;
	scanf("%d", &T);
	while (T--)
	{
		int n, m, k, x, y;
		scanf("%d%d%d", &n, &m, &k);
		for (int i = 1; i <= n; ++i) scanf("%d", &X[i]);
		for (int i = 1; i <= m; ++i) scanf("%d", &Y[i]);
		for (int i = 1; i <= k; ++i) {
			scanf("%d%d", &x, &y);
			p[i] = { x, y };
		}
		lcnt.clear(); rcnt.clear();
		sort(X + 1, X + n + 1);
		sort(p + 1, p + k + 1, mycmpl);
		int j = 2;
		ll ans = 0;
		for (int i = 1; i <= k;) {
			if (p[i].first == X[j - 1]) ++i;
			else if (X[j - 1] < p[i].first && p[i].first < X[j]) {
				if (!lcnt.count(p[i].second)) {
					lcnt[p[i].second] = 1;
				}
				else ++lcnt[p[i].second];
				++i;
			}
			else if(lcnt.size()){
				ll sum = 0;
				for (auto c : lcnt) {
					sum += c.second;
				}
				for (auto c : lcnt) {
					sum -= c.second;
					ans = ans + c.second * sum;
				}
				lcnt.clear();
				++j;
			}
			else ++j;
		}
		if (lcnt.size()) {
			ll sum = 0;
			for (auto c : lcnt) {
				sum += c.second;
			}
			for (auto c : lcnt) {
				sum -= c.second;
				ans = ans + c.second * sum;
			}
		}

		sort(Y + 1, Y + m + 1);
		sort(p + 1, p + k + 1, mycmpr);
		j = 2;
		for (int i = 1; i <= k;) {
			if (p[i].second == Y[j - 1]) ++i;
			else if (Y[j - 1] < p[i].second && p[i].second < Y[j]) {
				if (!rcnt.count(p[i].first)) {
					rcnt[p[i].first] = 1;
				}
				else ++rcnt[p[i].first];
				++i;
			}
			else if (rcnt.size()) {
				ll sum = 0;
				for (auto c : rcnt) {
					sum += c.second;
				}
				for (auto c : rcnt) {
					sum -= c.second;
					ans = ans + c.second * sum;
				}
				rcnt.clear();
				++j;
			}
			else ++j;
		}
		if (rcnt.size()) {
			ll sum = 0;
			for (auto c : rcnt) {
				sum += c.second;
			}
			for (auto c : rcnt) {
				sum -= c.second;
				ans = ans + c.second * sum;
			}
		}
		printf("%lld\n", ans);
	}
	system("pause");
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值