Codeforces Round 996 (Div. 2)

前言
这把打爽了,因为c题写错一个地方然后眼瞎一个小时没看见,直接掉大分。而且今天状态貌似很差,代码总是写错,ab都是莫名wa一发。

A. Two Frogs

题意:Alice和Bob在[1,n]这个区间玩,一开始Alice在a,Bob在b,他们轮流走,每个人可以往左或者往右,但不能越界,也不能走到另一个人的位置,Alice先走。不能走的输, 问Alice能不能赢。

想一个必赢的情况,如果Alice走一步能走到Bob旁边,那么Bob是不是只能往另一边走,Alice就可以赶过去,又变成这个情况,最后Bob会被赶到边界无法行动,这样Alice就赢了。
拓展一下,如果两个人中间隔了奇数个点,那么如果他们一起靠近,最后是不是变成必赢的情况,如果Bob不想靠近,往另一边走,那么两个人之间距离不变,又是这个情况,Bob只能不断走到边上,最后不得不往Alice这边走。所以如果|a - b| 是奇数,Alice必赢。
如果距离是偶数呢?Alice如果往Bob走,那么就成了Bob先手距离为奇数,Bob必赢。如果Alice往另一边走,一样的,Bob追她,他们之间距离不变,Alice还是得往Bob这一边走。
所以|a-b|为奇数Alice赢,否则Bob赢。

点击查看代码
void solve() {
    int n, a, b;
    std::cin >> n >> a >> b;
    if (std::abs(a - b) % 2 == 1) {
    	std::cout << "NO\n";
    } else {
    	std::cout << "YES\n";
    }
}

B. Crafting

题意:给你一个初始数组a和一个目标数组b,你要让初始数组每个位置的值大于等于目标数组对应位置的值,每次操作时选择一个i,让除i外的位置都减一,然后第i个位置加1。

最多只能有一个需要加的位置,否则你这个位置加好了,其他位置要加的话你就要减回去,这样反复肯定完成不了。
考虑只有一个要加的怎么做,假设他要加x个,那么其他都要减x个,如果对于所有的ai>=bi的情况对ai-bi取最小值min,那么min要大于等于x,因为其他所有值都会减x,所有值应该可以承受减去这个x,否则无解。
如果一个需要的都没有直接就是yes。

点击查看代码
void solve() {
    int n;
    std::cin >> n;
    std::vector<i64> a(n), b(n);
    for (int i = 0; i < n; ++ i) {
    	std::cin >> a[i];
    }

    for (int i = 0; i < n; ++ i) {
    	std::cin >> b[i];
    }

    i64 cnt = 0, need = 0, min = 1e18;
    for (int i = 0; i < n; ++ i) {
    	if (a[i] >= b[i]) {
    		min = std::min(min, a[i] - b[i]);
    	} else {
    		++ cnt;
    		need += b[i] - a[i];
    	}
    }

    if (cnt == 0 || (cnt == 1 && min >= need)) {
    	std::cout << "YES\n";
    } else {
    	std::cout << "NO\n";
    }
}

C. The Trail

题意:给你一个矩阵,有一条路径上的数需要你去填,要求填完后满足每行每列总和相等。

我们从这一行或者这一列只有这一个格子没填的格子开始填,假设目标总和是x,对应行列已有总和是sum,那么a[x][y] = x - sum, 如果他这一行或者这一列还有要填的, 对应行列总和已知为sum',那么这个a[x'][y'] = x - (sum' + x - sum)如果这个x'y'又有这一行或者这一列还有要填的,我们算下去发现,假设一个数要填的等于他之前行列填好的个数为cnt,那么它的值为(1-cnt)*x+sum,于是发现x取0不影响总和,一定满足。
用set和队列模拟即可,每次填一行或者一列只有这一个格子没填的格子。

点击查看代码
void solve() {
    int n, m;
    std::cin >> n >> m;
    std::string s;
    std::cin >> s;
    std::vector a(n, std::vector<i64>(m));
    std::vector<i64> sumr(n), sumc(m);
    for (int i = 0; i < n; ++ i) {
    	for (int j = 0; j < m; ++ j) {
    		std::cin >> a[i][j];
    		sumr[i] += a[i][j];
    		sumc[j] += a[i][j];
    	}
    }

    std::vector<std::multiset<int> > rs(n), cs(m);
    {
    	int x = 0, y = 0;
    	rs[0].insert(0);
    	cs[0].insert(0);
    	for (auto & c : s) {
    		if (c == 'D') {
    			++ x;
    		} else {
    			++ y;
    		}

    		rs[x].insert(y);
    		cs[y].insert(x);
    	}
    }

    i64 tag = 0;
    std::queue<std::pair<int, int> > q;
    for (int i = 0; i < n; ++ i) {
    	if (rs[i].size() == 1) {
    		q.push({i, -1});
    	}
    }

    for (int i = 0; i < m; ++ i) {
    	if (cs[i].size() == 1) {
    		q.push({-1, i});
    	}
    }

    while (q.size()) {
    	auto [x, y] = q.front(); q.pop();
    	if (x == -1) {
    		if (cs[y].size() == 0) {
    			continue;
    		}
    		x = *cs[y].begin();
    		// std::cout << x << " " << y << "\n";
    		a[x][y] = tag - sumc[y];
    		sumr[x] += a[x][y];
    		sumc[y] += a[x][y];
    		rs[x].extract(y);
    		cs[y].extract(x);
    		if (rs[x].size() == 1) {
    			q.push({x, -1});
    		}

    		if (cs[y].size() == 1) {
    			q.push({-1, y});
    		}
    	} else {
    		if (rs[x].size() == 0) {
    			continue;
    		}
    		y = *rs[x].begin();
    		// std::cout << x << " " << y << "\n";
    		a[x][y] = tag - sumr[x];
    		sumr[x] += a[x][y];
    		sumc[y] += a[x][y];
    		rs[x].extract(y);
    		cs[y].extract(x);
    		if (rs[x].size() == 1) {
    			q.push({x, -1});
    		}

    		if (cs[y].size() == 1) {
    			q.push({-1, y});
    		}
    	}
    }

    for (int i = 0; i < n; ++ i) {
    	for (int j = 0; j < m; ++ j) {
    		std::cout << a[i][j] << " \n"[j == m - 1];
    	}
    }
}

D. Scarecrow

赛后补题

题意:一个乌鸦在0的位置,有n个稻草人分布在数轴上,如果某一时刻有一个稻草人位置y小于等于乌鸦的位置x,并且距离小于k,那么乌鸦就会跳到y+k,如果跳了后还有满足条件的稻草人,就一直跳,这个跳跃是一瞬间的事情,你可以安排每一时刻每个稻草人是往左一格还是往右一格还是不动,求让稻草人位置大于等于L的最短时刻*2。

观察样例发现,之所以要乘上二是因为会有0.5这个小数,那么我们先把所有位置都*2,这样相当于我们以0.5为一次操作单位,答案更好算,不用担心浮点数。然后对于每个稻草人, 我们可以先假设它不动, 要用它的时候再让它动.
这题是一道模拟题, 我们需要存一个当前时间time,当前乌鸦位置pos,以及当前处理到的稻草人i。
一开始,如果前面没有稻草人我们应该让第一个稻草人移到0的位置,这样用时time = a0, 初始位置pos就是k。
如果pos ai,那么为了让乌鸦更往后,应该让当前稻草人往后time格,但不要超过pos,这样pos = min(pos, ai + time) + k。 为什么一定能加k,因为我们按顺序处理的稻草人,如果有pos ai, 那么是不是从i前面位置加了k过来的,ai肯定ai1, 所以pos - ai1 == k的话,pos - ai肯定小于k。
如果pos < ai,那么我们应该让i-1和i同时靠近pos,由于我们肯定希望i-1个把乌鸦顶到后面之后正好到ai的位置,这样pos就可以等于ai + k。我们先让ai = max(ai - time, pos),那么因为pos = ai1 + k,那么左边可以一步步让pos + 1,右边同时一步步靠近,这样pos和ai重合需要(ai - pos) / 2秒。然后pos = pos + (ai - pos) / 2 + k, time += (ai - pos) / 2.
最后如果还没有到L的话,就由最后应该把他一步一步顶过去就行了, time += L - pos.
代码参考了jiangly的.

点击查看代码
void solve() {
	int n, k, L;
	std::cin >> n >> k >> L;
	k *= 2;
	L *= 2;
	std::vector<int> a(n);
	for (int i = 0; i < n; ++ i) {
		std::cin >> a[i];
		a[i] *= 2;
	}

	int time = a[0], pos = k;
	for (int i = 1; i < n; ++ i) {
		if (a[i] > pos) {
			a[i] = std::max(pos, a[i] - time);
			int t = (a[i] - pos) / 2;
			time += t;
			pos = pos + t + k;
		} else {
			a[i] = std::min(pos, a[i] + time);
			pos = a[i] + k;
		}
	}

	if (pos < L) {
		time += L - pos;
	}

	std::cout << time << "\n";
}
posted @   maburb  阅读(458)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 推荐几款开源且免费的 .NET MAUI 组件库
· 实操Deepseek接入个人知识库
· 易语言 —— 开山篇
· Trae初体验
点击右上角即可分享
微信分享提示