Codeforces Round 989(Div. 1 + Div. 2)[Rayan Programming Contest 2024 - Selection]

算法题思路及证明:最小公倍数与贪心等

A. King Keykhosrow's Mystery

思路:看懂题后知道只要求他们的最小公倍数就行了,而难点就在于对其的证明。(搜了一下好像没人讲证明过程,这里我斗胆讲一下)。

证明:

首先列出m成立的两个条件逐一分析:

条件一:m 必须大于或等于 𝑎 和 𝑏 中的至少一个。

解释:保证了题目有意义,不然m可以很小,同时小于a、b,比如1、2,等的对他们的余数都相等,所以要有此条件。

条件二:𝑚 除以 𝑎 的余数必须等于 𝑚 除以 𝑏 的余数。

解释:这可以通过数学上的同余关系来理解。列出下列式子:

公式一:m=k*a+r

公式二:m=l*b+r

其中 𝑘 和 𝑙 是整数,𝑟 是余数。这意味着 𝑚 可以被 𝑎 和 𝑏 的某个公倍数表示,且余数相同。 那么我们要最小的m,肯定是余数 𝑟 为0,那么公式简化完不就是m是a,b的倍数,最小不就是最小公倍数。

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long

int gcd(int a, int b){
    if(b == 0) return a;
    return gcd(b, a % b);
}

int lcm(int a, int b){
    return (a / gcd(a, b)) * b;
}

void solve() {
    int a, b;
    cin >> a >> b;
    int l = lcm(a, b);
    cout << l << endl;
}

signed main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    int t = 1;
    cin >> t;
    while (t--) {
        solve();
    }
    
    return 0;
}

还有一种暴力做法,从a、b的较小值开始遍历,直到满足条件为止,这题数据范围较小,能过。

B. Rakhsh's Revival

思路:贪心,从头开始遍历,遇到不满足的区间就+1。

#include <bits/stdc++.h>
using namespace std;
#define int long long

void solve()
{
    int n, m, k;
    cin >> n >> m >> k;
    string s;
    cin >> s;
    int num = 0, res = 0;
    for (int i = 0; i<n; i++){
        if (s[i]=='1') num = 0;
        else if (s[i]=='0'){
            num++;
            if (num >= m){
                res++;
                num = 0;
                i+=k-1;
            }
        }
    }
    cout << res << endl;
    
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }
    
    return 0;
}

C. Trapped in the Witch's Labyrinth

思路:感觉会有更好的思路,这里我讲一下我的思路。我主要想的是用两次dfs。第一次把能跑出去的都记录下来,check标为1,不能跑出来的标0(包括遇到'?'未知的地方);第二次遍历check为0的地方,如果是已知地方那么肯定要+1,如果是'?'未知地方,那么检查它的上下左右四个方向是否存在有不能走通的方向,如果有就+1,如果四个方向都能走通(check为1),那么跳过。

代码:

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

int dx[4] = {1, 0, 0, -1};
int dy[4] = {0, 1, -1, 0};
void solve() {
	ll n, m;
	cin >> n >> m;
	vector<vector<char>> g(n, vector<char>(m));
	vector<vector<bool>> vis(n, vector<bool>(m, false));
	vector<vector<int>> check(n, vector<int>(m, 0));
	for (ll i = 0; i < n; i++) {
		for (ll j = 0; j < m; j++) {
			cin >> g[i][j];
		}
	}
	function<bool(int, int)> dfs = [&](int x, int y) -> bool {
		if (x < 0 || x >= n || y < 0 || y >= m) {
			return 1;
		}
		if (vis[x][y]) {
			return check[x][y];
		}
		if (g[x][y] == '?') return 0;
		vis[x][y] = true;
		if (g[x][y] == 'U') check[x][y] |= dfs(x-1, y);
		else if (g[x][y] == 'D') check[x][y] |= dfs(x+1, y);
		else if (g[x][y] == 'L') check[x][y] |= dfs(x, y-1);
		else if (g[x][y] == 'R') check[x][y] |= dfs(x, y+1);
		return check[x][y];
	};
	for (ll i = 0; i < n; i++) {
		for (ll j = 0; j < m; j++) {
			if (!vis[i][j]) {
				dfs(i, j);
			}
		}
	}
//	for (ll i = 0; i < n; i++) {
//		for (ll j = 0; j < m; j++) {
//			cout << check[i][j] << " \n"[j==m-1];
//		}
//	}
	int ans = 0;
	for (int i = 0; i<n; i++)
	{
		for (int j = 0; j<m; j++){
			if (check[i][j] == 0 && g[i][j]!='?'){
				ans++;
			}
			else if (check[i][j] == 0 && g[i][j] == '?'){
				int pp = 1;
				for (int k = 0; k<4; k++){
					int tx = i+dx[k], ty = j+dy[k];
					if (tx >= 0 && tx <n && ty >=0 && ty < m){
						pp&=check[tx][ty];
					}
					
				}
				if (pp == 0) ans++;
				
			}
		}
	}
	cout << ans << endl;
}

int main() {
	ios::sync_with_stdio(false);
	cin.tie(0);
	cout.tie(0);
	
	int t = 1;
	cin >> t;
	while (t--) {
		solve();
	}
	return 0;
}

这里我保留了输出check数组的注释,感兴趣的可以试一下。

D. Darius' Wisdom

思路:题意很好懂,简单的思维在代码量上还是很大的。

这里我们知道如果应该是2的位置上为0,需要2次操作,为1,需要一次操作,应该为1的位置上是0的话需要一次操作。这里其实用到了了贪心,我们肯定从二位置的上来开始弄,因为他会影响0,1的位置,再严谨得写代码。

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
void solve()
{
    int n, m, k;
    cin >> n;
    vector<int> a(n);
    for (int i = 0; i<n; i++){
        cin >> a[i];
    }
    array<set<int>, 3> pos;
    for (int i = 0; i<n; i++) pos[a[i]].emplace(i);
    vector<pair<int, int>> ans;

    auto modify = [&](int i){
        if (!pos[2].empty()){
            if (a[i] == 2) pos[2].erase(i);
            else if (a[i] == 1){
                int temp = *pos[2].begin();
                swap(a[i], a[temp]);
                pos[2].erase(temp);
                pos[1].erase(i);
                pos[1].emplace(temp);
                ans.emplace_back(i, temp);
            }
            else {
                int tem1 = *pos[1].begin(), tem2 = *pos[2].begin();
                swap(a[i], a[tem1]);
                swap(a[i], a[tem2]);
                pos[2].erase(tem2);
                pos[1].erase(tem1);
                pos[1].emplace(tem2);
                pos[0].erase(i);
                pos[0].emplace(tem1);
                ans.emplace_back(i, tem1);
                ans.emplace_back(i, tem2);
            }
        }
        else{
            if (a[i] == 1) pos[1].erase(i);
            else{
                int temp = *pos[1].begin();
                swap(a[i], a[temp]);
                pos[1].erase(temp);
                pos[0].erase(i);
                pos[0].emplace(temp);
                ans.emplace_back(i, temp);
            }
        }
    };

    for (int i = n-1; i>=0; i--){
        if (pos[1].empty() && pos[2].empty()) break;
        modify(i);
    }
    cout << ans.size() << endl;
    for (auto [fi, se] : ans){
        cout << fi+1 << " " << se+1 << endl;
    }
}

signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);
    
    int t = 1;
    cin >> t;
    while (t--)
    {
        solve();
    }
    
    return 0;
}

(如果有帮助请点赞或评论支持,有问题请指正,其他问题请评论交流...) 

都看到这里了真不点个赞吗

7-8 敲笨钟 (15分) 微博上有个自称“大笨钟V”的家伙,每天敲钟催促码农们爱惜身体早点睡觉。为了增加敲钟的趣味性,还会糟改几句古诗词。其糟改的方法为:去网上搜寻压“ong”韵的古诗词,把句尾的三个字换成“敲笨钟”。例如唐代诗人李贺有名句曰:“寻章摘句老雕虫,晓月当帘挂玉弓”,其中“虫”(chong)和“弓”(gong)都压了“ong”韵。于是这句诗就被糟改为“寻章摘句老雕虫,晓月当帘敲笨钟”。 现在给你一大堆古诗词句,要求你写个程序自动将压“ong”韵的句子糟改成“敲笨钟”。 输入格式: 输入首先在第一行给出一个不超过 20 的正整数 N。随后 N 行,每行用汉语拼音给出一句古诗词,分上下两半句,用逗号 , 分隔,句号 . 结尾。相邻两字的拼音之间用一个空格分隔。题目保证每个字的拼音不超过 6 个字符,每行字符的总长度不超过 100,并且下半句诗至少有 3 个字。 输出格式: 对每一行诗句,判断其是否压“ong”韵。即上下两句末尾的字都是“ong”结尾。如果是压此韵的,就按题面方法糟改之后输出,输出格式同输入;否则输出 Skipped,即跳过此句。 输入样例: 5 xun zhang zhai ju lao diao chong, xiao yue dang lian gua yu gong. tian sheng wo cai bi you yong, qian jin san jin huan fu lai. xue zhui rou zhi leng wei rong, an xiao chen jing shu wei long. zuo ye xing chen zuo ye feng, hua lou xi pan gui tang dong. ren xian gui hua luo, ye jing chun shan kong. 输出样例: xun zhang zhai ju lao diao chong, xiao yue dang lian qiao ben zhong. Skipped xue zhui rou zhi leng wei rong, an xiao chen jing qiao ben zhong. Skipped Skipped
7-7 简易连连看 (15分) 本题要求实现一个简易连连看游戏模拟程序给定一个22N的方阵网格游戏盘面,每个格子中放置一些符号。这些符号一定是成对出现的,同一个符号可能不止一对。程序读入玩家给出的一对位置(x ​1 ​​ ,y ​1 ​​ )(x ​2 ​​ ,y ​2 ​​ ),判断这两个位置上的符号是否匹配。如果匹配成功,则将两个符号消为“*”并输出消去后的盘面;否则输出“Uh-oh”。若匹配错误达到3次,则输出“Game Over”并结束游戏。或者当全部符号匹配成功,则输出“Congratulations!”,然后结束游戏。 输入格式: 输入在一行中给一个正整数N(<5)。随后2N行,每行2N个大写英文字母(其间以1个空格分隔),表示游戏盘面盘面之后给出一个正整数K,随后K行,每行按照格式“x ​1 ​​ y ​1 ​​ x ​2 ​​ y ​2 ​​ ”给出一个玩家的输入。注意格子的行、列编号是从12N。 输出格式: 根据玩家的每一步输入,输出相应的结果。输出盘面时注意,每行字符间以1个空格分隔,行末不得有多余空格。 输入样例12 I T I T Y T I A T A T Y I K K T 11 1 1 1 3 4 2 4 3 3 1 4 2 2 2 1 2 3 1 2 4 4 4 3 1 2 1 3 4 3 3 1 4 4 1 2 3 2 4 3 2 1 1 2 2 输出样例1: * T * T Y T I A T A T Y I K K T * T * T Y T I A T A T Y I * * T Uh-oh * * * T Y * I A T A T Y I * * T Uh-oh * * * T Y * I A * A T Y I * * * * * * T * * I A * A T * I * * * * * * * * * I A * A * * I * * * * * * * * * * A * A * * * * * * Congratulations! 输入样例22 I T I T Y T I A T A T Y I K K T 5 1 1 4 4 1 1 2 3 1 1 2 3 2 2 4 1 2 2 3 3 输出样例2: Uh-oh * T I T Y T * A T A T Y I K K T Uh-oh Uh-oh Game Over
Codeforces Round 1036 是一场同时面向 Div.1Div.2 参赛者的比赛,通常这类比赛会包含多个具有挑战性的编程题目,涵盖算法、数据结构、数学等多个领域。比赛的题解和题目信息可以帮助参赛者回顾解题思路,提升编程能力。 ### 比赛基本信息 - **比赛名称**:Codeforces Round #1036 (Div. 1 and Div. 2) - **比赛时间**:具体时间为 UTC+X(根据实际举办日期和时间表) - **比赛链接**:[Codeforces 官方页面](https://codeforces.com/contest/1343) - **题解发布位置**:通常在比赛结束后不久,官方或社区成员会在 Codeforces 博客、GitHub 或其他技术平台上发布题解。 ### 题目类型与难度分布 该轮比赛通常包括 5 到 7 道题目,难度从简单实现到复杂算法不等。例如: - **A题**:通常是简单的模拟或数学问题。 - **B题**:可能涉及字符串处理或基础贪心策略。 - **C题**:中等难度,可能需要掌握基本的数据结构如数组、排序等。 - **D题及以后**:较高难度,可能涉及图论、动态规划、数论等高级算法。 ### 参赛情况与亮点 - **参与人数**:通常超过 10,000 名选手参加。 - **热门话题**:比赛中某些题目可能会引发广泛讨论,尤其是那些需要用到巧妙构造或优化技巧的问题。 - **知名选手表现**:顶尖选手如 tourist、Um_nik 等通常会以极快的速度完成所有题目,并占据排行榜前列。 ### 示例代码片段 以下是一个典型的 Codeforces 题目解法示例,适用于某道中等难度题目: ```cpp #include <bits/stdc++.h> using namespace std; int main() { int t; cin >> t; while(t--) { long long l, r; cin >> l >> r; // 假设 e 是一个预处理好的符合条件的数组 // 使用二分查找来统计区间 [l, r] 内的有效数字个数 long long ans = upper_bound(e.begin(), e.end(), r) - lower_bound(e.begin(), e.end(), l); cout << ans << endl; } return 0; } ``` ### 题解资源推荐 - **Codeforces 官方博客**:通常会有详细的题解和作者说明。 - **GitHub 仓库**:许多参赛者会将自己的解法上传至 GitHub,便于他人学习。 - **知乎专栏 / 优快云 / 博客园**:中文社区中也常有高质量的赛后总结与分析文章。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wirepuller_king

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值