Codeforces Round 992 (Div. 2) 题解

Codeforces Round 992 (Div. 2) 题解

A. Game of Division

给定长为n的数组和整数k,AB两人玩一个游戏,A先任选择一个数,B任选另一个数,如果两数之差被k整除 则A赢否则B赢。1<=n,k<=100

  • 题解1。由于范围较小,可以暴力二维枚举i,j 要求(i!=j)的情况下如果有符合的则A赢 复杂度\(O(n^2)\)
  • 题解2。可以枚举数组记录每个数mod k,如果最后某个mod个数为1 则A赢
    碰到整除相关的,枚举余数往往是个很好的思路。复杂度\(O(n+k)\)
void solve()
{
	int n, k; cin >> n >> k;
	vector<vector<int>> res(k);
	for (int i = 0; i < n; i++) {
		int x; cin >> x;
		res[x % k].push_back(i + 1);
	}
	for (int i = 0; i < k; i++) {
		if (res[i].size() == 1) {
			cout << "yes" << endl;
			cout << res[i][0] << endl;
			return;
		}
	}
	cout << "no" << endl;
}

B. Paint a Strip

给定长为n的初始全0数组,有两种操作。
操作1,任选一个下标把0变成1。
操作2,任选一个子数组,如果子数组两端都是1,且1的总个数>=0的总个数,则把子数组全部变成1。
问把所有元素变成1所需的操作1的最少次数

显然,操作2是一个倍增操作。而操作1用于给操作2末尾变1用于启动。
操作过程:
开始执行一次操作1 把第一个元素变成1。
保持前缀始终全是1,然后跳过x个0,末尾添加1。把整个部分变成1。

void solve()
{
	int n; cin >> n;
	int ans = 1;
	int cur = 1;
	while (cur < n) {
		ans++;
		cur = (cur + 1) * 2;
	}
	cout << ans << endl;
}

C. Ordered Permutations

对于一个长为n的排列p,S(p)表示p所有子数组的最小值的和。
设所有S(p)的最大值为M,所有S(p)=M的排列按字典序排列,输出第k个,如果不足k个,输出-1。

如图,一共有4个有效排列(=10的),所以n=3,k=3时输出[2,3,1]

典型的CF思维题。正难则反。
从大到小处理数组,先放上n,然后逐个处理。
每个数由于比前面的数都小,所以只能放在数组的两端,每次有两种选择方式,所以总共可能有\(2^{n-1}\)种排列。
以n=4为例,开始队列为{4},3放4前面还是后面,形成{3,4}或者{4,3}在字典序中顺序差1。
然后再放2,此时由于3和4的组合有两种,所以2会导致字典序差2。
依次类推,每个数字放前后,取决于当前k的二进制位置上是1还是0。

using i64 = long long;
void solve()
{
    i64 n, k; cin >> n >> k;
    if (n <= 60 && (1LL << (n - 1)) < k) {
        cout << -1 << endl;
        return;
    }
    deque<i64> q{ n };
    k--; n--;
    while (n > 0)
    {
        if (k % 2 == 1) q.push_back(n);
        else q.push_front(n);
        k >>= 1;
        n--;
    }
    for(auto v : q) cout << v << " ";
    cout << endl;
}

D. Non Prime Tree

给定一棵树,节点编号[1-n]。然后要求构建长为n的数组\(array\)\(array[i]\)对应到数上点权。要求:
\(array\)中所有数字不同
\(array[i]<=2n\)
任意边的点权差绝对值不是质数

首先,不是质数,差为1也是可以的!
结论是必然存在

  • 题解1 直接模拟即可。
    全局维护当前数字cur,dfs遍历节点
    ans[u] = cur++。枚举子节点v的时候,判定cur-ans[u]是否是质数,如果是,就一直增加cur直到不是为止。
    事实上,2n的范围很宽松,以至于不用质数那么严谨,只需要找到下一个cur-ans[u]为1或者>2的偶数即可。
    严谨的证明不会。可能这就是感觉吧。
void solve()
{
	int n; cin >> n;
	vector<vector<int>> g(n);
	for (int i = 0; i < n - 1; i++) {
		int u, v; cin >> u >> v;
		u--; v--;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	vector<int> ans(n);
	int cur = 1;
	auto dfs = [&](auto&& self, int u, int p)->void {
		ans[u] = cur++;
		for (auto v : g[u]) {
			if (v == p) continue;
			int next = cur;
			while (true) {
				int d = next - ans[u];
				if (d == 1) break;
				if (d == 2 || d % 2 == 1) next++;
				else break;
			}
			cur = next;
			self(self, v, u);
		}
	};
	dfs(dfs, 0, -1);
	for (auto v : ans) cout << v << " ";
	cout << endl;
}
  • 题解2 层序遍历,偶数层设置 2,4,6... 奇数层设置2n,2n-2,2n-4...
    这样至多出现一组差为2的点对(u->v) 此时v必然为叶节点,设置ans[v] = ans[v] - 1即可。

E. Control of Randomness

给定一棵树,根节点为1,有个机器人从v点出发,向根节点移动,初始step=1,另外有p个硬币。具体移动如下:
如果step为奇数,则直接向根节点移动。
否则要么花费一个硬币,向根节点移动,要么随机向当前节点的任意相邻节点移动。
有q个询问,每次查询如果从v节点开始,有p个硬币,计算最佳策略移动到根节点的期望step。
1<=n,q<=2e3 注意n q的范围

对于从v->1的整条路径,奇数步的位置固定是向上移动的,可以忽略。
对于偶数步,设当前节点v的子节点个数为x,注意加上一个父节点,v的相邻节点有(x+1)个
设f[i]表示节点i的最终期望步数,则有

\[f(v) = \begin{cases} \frac{1}{x+1}f[u], & \frac{1}{x+1}的概率随机到直接向上\\ \frac{x}{x+1}(f[v]+2), & \frac{x}{x+1}的概率随机到其它节点,然后再次回到v节点,相当于f[v]+2 \\ \end{cases} \]

直接两式相加计算可得\(f[v]=f[u]+2x\)

注意我们有p个硬币,很明显,我们要在分支较多的节点上花费掉硬币。
由于数据范围较小,每个查询可以独立的暴力计算。

void solve()
{
	int n, q; cin >> n >> q;
	vector<vector<int>> g(n);
	for (int i = 0; i < n - 1; i++) {
		int u, v; cin >> u >> v;
		u--; v--;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	vector<int> fa(n);
	vector<int> size(n);
	auto dfs = [&](auto&& self, int u, int p)->void {
		fa[u] = p;
		for (auto v : g[u]) {
			if (v == p) continue;
			self(self, v, u);
			size[u]++;
		}
	};
	dfs(dfs, 0, -1);
	vector<int> ans(q);
	for (int i = 0; i < q; i++) {
		int v, p; cin >> v >> p; v--;
		vector<int> xs;
		int step = 1;
		while (v != 0) {
			if (step % 2 == 0) xs.push_back(size[v]);
			v = fa[v];
			step++;
		}
		int ans = step - 1;
		sort(xs.begin(), xs.end(), greater<int>());
		for (int i = 0; i < xs.size(); i++) {
			if (i >= p) ans = ans + xs[i] * 2;
		}
		cout << ans << endl;
	}
}

F. Number of Cubes 这是我能做的题?

posted @ 2024-12-11 20:40  云上寒烟  阅读(207)  评论(0编辑  收藏  举报