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;
}

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

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

wirepuller_king

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

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

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

打赏作者

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

抵扣说明:

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

余额充值