首页 > 其他分享 >CF1893E题解

CF1893E题解

时间:2023-10-23 17:45:18浏览次数:33  
标签:frac int 题解 sum CF1893E 碎片 MAXN 集合

  • 分析

    第一眼:博弈论。
    第二眼:呃……贪心?
    实际:DP。
    首先想这个游戏大抵存在必胜策略,否则不会让我们求。

    思考先手必胜条件,就是如何让这个数组最后只剩下一个数。
    设数列之和为\(sum\)。
    发现每次操作给两个数减的数字是一样的。那么对于每次操作,\(\Delta sum\)都为两者之间更少的数的二倍。
    所以假设每次取的更小的数为 \(x\),那么如果\(\exists\sum x= \frac{sum}{2}\),那么数组最后的两个数就会在一次操作后都为零,显然此时先手不必胜。
    而如果不为二分之一,在最后一次操作就会出现只剩下一个数的情况,所以此时先手必胜,而且无论如何取都会赢。

    思考如何计算每次取的更小的数的和 \(\sum x\)。
    每次取的更小的数有两种情况,一种是原本的 \(a_i\),一种是 \(a_i\) 减过几次的 \(a_{i}'\),因为更小的数是减数的集合,所以对于每个减过的数都会存储在这个集合中,所以对于每个减数“碎片”都存在于集合。
    如果“碎片”是\(a_{i}\),那么在集合里删掉这个数,将“碎片”拼回被减数。
    如果“碎片”是\(a_{i}'\),那么我们可以任意保留\(a_{i}\)或者原被减数中的一个,因为一定存在一种方案可以将所有碎片都拼回去。
    为什么?因为我们考虑一个个放回去,最后得到的初始取数集合一定是全是\(a_{i}\)(或者说你取的第一个数不可能是“碎片”),即不存在任何“碎片”的集合,所以“碎片”一定都会拼回去。
    所以\(\exists\sum x= \frac{sum}{2}\)就变成了\(\exists\sum a_{i}= \frac{sum}{2}\)。

    思考后手必胜情况。
    因为已知\(\sum x\)为\(\sum a_i\),所以我们每次是减一个在这个集合里的数,同时也要减一个不在这个集合里的数。
    所以我们可以每次选一个合法且与先手选的数所在集合不同的数即可。

    然后我们现在要找一个集合使得\(\sum a_i = \frac{sum}{2}\)。
    用背包可以解决,在转移的时候使用bitset可以存储取数情况。
    空间复杂度\(\mathcal{O(n^3)}\),时间复杂度\(\mathcal{O(\frac{n^4}{w})}\),可以通过本题。

  • 代码

#include <iostream>
#include <bitset>
using namespace std;
constexpr int MAXN(307);
bitset <MAXN> fs[MAXN * MAXN];
int a[MAXN], f[MAXN * MAXN], blg[MAXN];
int n, sum, t;
inline void read(int &temp) { cin >> temp; }
int main() {
	ios::sync_with_stdio(false);
	read(n);
	for (int i(1); i <= n; ++i)  read(a[i]), sum += a[i];
	f[0] = 1;
	for (int i(1); i <= n; ++i) {
		for (int j(sum); j >= a[i]; --j) {
			if (f[j])  continue;
			f[j] |= f[j - a[i]];
			if (!f[j])  continue;
			fs[j] = fs[j - a[i]], fs[j][i] = 1;
		}
	}
	if (sum % 2 == 0 && f[sum / 2]) {
		for (int i(1); i <= n; ++i)  if (fs[sum / 2][i])  blg[i] = 1;
		cout << "Second" << endl;
		while (1) {
			read(t);
			if (!t)  break;
			for (int j(1); j <= n; ++j) {
				if (blg[j] == (blg[t] ^ 1) && a[j] && j != t) {
					cout << j << endl;
					int mns = min(a[t], a[j]);
					a[t] -= mns, a[j] -= mns;
					break;
				}
			}
		}
	}
	else {
		cout << "First" << endl;
		while (1) {
			int cz(0);
			for (int j(1); j <= n; ++j) {
				if (a[j]) {
					cout << j << endl;
					cz = j;
					break;
				}
			}
			read(t);
			if (!t)  break;
			int mns = min(a[t], a[cz]);
			a[t] -= mns, a[cz] -= mns;
		}
	}
	return 0;
}

标签:frac,int,题解,sum,CF1893E,碎片,MAXN,集合
From: https://www.cnblogs.com/Kazdale/p/17783061.html

相关文章

  • CSP-S 2023 种树-题解
    CSP-S2023种树-题解闲话Mark.Down看错题面了,我一直以为树是倒着长的。题目描述给定一棵树,每天可以选择一个与已种树的地块相连的地块种树,每棵树每天会长\(max(1,c_i\timesx+b_i)\)米(\(x\)代表从任务开始第一天的天数),问最少多少天可以使\(\foralli\inn,Height_i\gea_i\)......
  • P8820(csp-s 2022 T4)题解
    背景:由于FZ考试因疫情取消,于是我们学校组织了线上测试。赛场连假做法都没打完,然后暴力忘记交了。。。题目链接参考博客题目评价:场切有点困难,但是76分比较容易。解法一眼\(ddp\),没话说。下面假设树以\(1\)为根。一次传输称作从一个点跳到另一个点。设询问的两个点为\(......
  • 题解合集
    CF1846:CF1846ACF1846BCF1846CCF1846DCF1846E......
  • CF1839D题解
    分析啊这道题就做得很难受了……手玩一下样例,不难发现答案就是分出\(k\)段不是单调上升序列的序列,求这些序列的最小长度和。显然有状态\(f_{l,r,k}\)表示\([l,r]\)序列分成\(k\)段的最小长度和。转移很好想,即枚举\(x\),\(y\)分别表示左区间的右端点以及段数,空间复杂度\(\math......
  • 题解:【CF1888E】 Time Travel
    题目链接刚从modinte那里学到的广义dijkstra。注意到一定不会有路径形如\(x\toy\tox\),这样等价于\(x\)在原地等上两个时刻,我们记\(d_i\)表示到达\(i\)节点需要的最少时间。建图,边权为当前这一条边在哪一个历史时刻。然后用一个set来存下每个历史时刻在第几次时间......
  • CSP-S 2023 消消乐-题解
    CSP-S2023消消乐-题解闲话省流:longlong模拟pair好抽象的题,可惜考场上没做出来。感觉其实是一个挺有趣的题的。题目描述小L现在在玩一个低配版本的消消乐,该版本的游戏是一维的,一次也只能消除两个相邻的元素。现在,他有一个长度为\(n\)且仅由小写字母构成的字符串。我......
  • CF1839A题解
    分析可以很容易地想到如果只有1要求的话答案就是\(\lceil\frac{n}{k}\rceil\)。最优策略显然是在每个整除分块的第一位放一个1。思考加入2条件如何修改。显然当最后一块的大小不为1时,大于1的部分后缀和为0。所以需要在最后一位加入一个1。所以答案为\(\begin{cases}\lc......
  • [ZJOI2015] 地震后的幻想乡积分题解
    题意:给定一个无向图,边权为\([0,1]\)之间的随机变量。求图最小生成树最大边权的期望。\(n\le10\)。Soluion:Meatherm口诏:我都不知道这个东西怎么想出来的针对这道题,好像正常的方法是转计数然后斯特林反演+dp。但是如果想到概率理论,你就已经赢了很遗憾,我没想出来设最大边......
  • [题解]P9752 [CSP-S 2023] 密码锁
    这次CCF的行为过于迷惑了。思路首先发现只会有\(10^5\)种密码,考虑枚举它们,然后去check。假设当前密码是:\(p_1,p_2,p_3,p_4,p_5\)。如果它能从对于所有\(1\simn\)种错误的密码按照题目所述的操作得到,那么此密码就是合法的。假设我们现在判断当前密码能否由第\(i\)种......
  • 洛谷-P9779 题解
    正文对于每个选择题,都有两种状态,因此总状态数为\(2^n\)。请注意初始所有选择题都不选也是一个状态,不计入贡献,因此答案为\(2^n-1\)。代码:#include<iostream>usingnamespacestd;intmain(){longlongn;cin>>n;cout<<(1<<n)-1;}提交记录。......