2020杭电多校 Game

题目链接
题意
在 二 维 坐 标 上 , 有 n 个 点 , 起 点 固 定 , 双 方 轮 流 选 择 一 个 未 被 经 过 的 点 , 且 要 保 证 距 离 严 格 递 增 , 问 谁 会 w i n ? 在二维坐标上,有n个点,起点固定,双方轮流选择一个\\未被经过的点,且要保证距离严格递增,问谁会win? nwin?

思路
比 赛 时 想 破 头 皮 都 不 知 道 要 怎 么 处 理 , 一 直 想 把 它 转 化 成 N i m 游 戏 , 可 惜 失 败 了 ! 比赛时想破头皮都不知道要怎么处理,一直想把它转化成Nim\\游戏,可惜失败了! Nim
看 了 正 解 , 恍 然 大 悟 , 真 妙 。 原 来 这 个 游 戏 有 一 坨 必 胜 点 , 且 最 多 只 有 一 个 必 败 点 。 这 样 的 话 , 要 是 第 一 个 点 是 必 胜 点 , 肯 定 先 手 w i n , 要 是 第 一 个 点 是 必 败 点 , 那 么 不 管 先 手 怎 么 走 , 后 手 都 都 会 位 于 必 胜 点 , 所 以 后 手 w i n 涨 知 识 了 , 以 后 确 实 可 以 往 这 个 地 方 想 , 并 尝 试 构 造 看了正解,恍然大悟,真妙。原来这个游戏有一坨必胜点,且\\最多只有一个必败点。这样的话,要是第一个点是必胜点,肯定\\先手win,要是第一个点是必败点,那么不管先手怎么走,后手都\\都会位于必胜点,所以后手win\\涨知识了,以后确实可以往这个地方想,\\并尝试构造 win,win
如 何 证 明 并 且 找 出 这 些 必 胜 点 呢 ? 如何证明并且找出这些必胜点呢?
首 先 对 于 所 有 点 集 , 记 S 1 = { ( u , v ) ∣ d i s ( u , v ) 最 大 } 首先对于所有点集,记S_1=\{ (u,v)|dis(u,v)最大 \} S1={(u,v)dis(u,v)}
考 虑 S 1 中 点 对 ( u , v ) , 显 然 可 以 推 出 u 和 v 为 必 胜 态 为 什 么 ? 当 前 先 手 到 达 u , 那 么 他 可 以 把 u 进 行 转 移 到 v 那 么 后 手 无 路 可 走 , 所 以 先 手 必 胜 考虑S_1中点对(u,v),显然可以推出u和v为必胜态\\为什么?当前先手到达u,那么他可以把u进行转移到v\\那么后手无路可走,所以先手必胜 S1u,vuvuuv
结 论 1 : S 1 点 对 中 出 现 的 点 一 定 为 必 胜 状 态 结论1:S_1点对中出现的点一定为必胜状态 1S1
那 么 剩 下 的 点 是 什 么 状 态 呢 ? 那么剩下的点是什么状态呢?
考 虑 把 S 1 点 对 中 出 现 的 点 删 掉 , 同 样 的 子 问 题 考虑把S_1点对中出现的点删掉,同样的子问题 S1
记 S 2 = { ( u ′ , v ′ ) ∣ d i s ( u ′ , v ′ ) 最 大 } 记S_2=\{ (u',v')|dis(u',v')最大 \} S2={(u,v)dis(u,v)}
考 虑 S 2 中 点 对 ( u ′ , v ′ ) , 显 然 可 以 推 出 u ′ 和 v ′ 为 必 胜 态 为 什 么 ? 当 前 先 手 到 达 u ′ , 那 么 他 可 以 把 u ′ 进 行 转 移 到 v ′ 那 么 后 手 无 路 可 走 或 者 走 到 S 1 中 的 点 ( P a y − A t t e n t i o n ) 考虑S_2中点对(u',v'),显然可以推出u'和v'为必胜态\\为什么?当前先手到达u',那么他可以把u'进行转移到v'\\那么后手无路可走或者走到S_1中的点(Pay -Attention) S2u,vuvuuvS1(PayAttention)
如 果 后 手 无 路 可 走 , 那 么 先 手 必 胜 如果后手无路可走,那么先手必胜
如 果 后 手 走 到 S 1 中 的 点 , 那 么 先 手 走 到 必 胜 状 态 点 , 从 而 推 出 先 手 必 胜 如果后手走到S_1中的点,那么先手走到必胜状态点,从而推出\\先手必胜 S1
结 论 2 : S 2 点 对 中 出 现 的 点 一 定 为 必 胜 状 态 结论2:S_2点对中出现的点一定为必胜状态 2S2
推 论 : S i 中 的 点 一 定 为 必 胜 状 态 推论:S_i中的点一定为必胜状态 Si

如何写代码呢?
我 们 可 以 使 用 一 个 结 构 体 , 定 义 边 , 记 录 边 的 端 点 以 及 距 离 我们可以使用一个结构体,定义边,记录边的端点以及距离 使
肯 定 需 要 把 所 有 边 进 行 一 个 降 序 , 然 后 对 于 一 段 相 等 d i s , 进 行 分 段 , 然 后 进 行 处 理 。 肯定需要把所有边进行一个降序,然后对于一段相等dis,\\进行分段,然后进行处理。 dis
如 何 进 行 删 点 操 作 呢 ? 如何进行删点操作呢?
v i s [ i ] 表 示 i 点 在 S v i s [ i ] 集 合 中 , 倘 若 我 们 正 在 考 虑 S j , 对 于 每 一 条 边 , 我 们 考 虑 其 端 点 , 如 果 端 点 的 v i s < j 则 表 明 该 点 已 经 删 除 则 不 需 要 考 虑 这 条 边 。 vis[i]表示i点在S_{vis[i]}集合中,倘若我们正在考虑S_j,对于每一条\\边,我们考虑其端点,如果端点的visvis[i]iSvis[i]Sjvis<j

详情可见代码

#include 

using namespace std;
typedef long long ll;
const int maxn = 2500;
const int maxm = 5e6 + 10;
int n, tot;
struct edge{
	int u, v;
	ll dis;
	bool operator < (const edge &a)const{
		return dis > a.dis;
	}
}e[maxm];
int vis[maxn], dfn;
pair<int, int> p[maxn];
void init(){
	for(int i = 1; i <= n; i++){
		vis[i] = 0;
	}
	dfn = 0;
	tot = 0;
}
void print(){
	for(int i = 1; i <= tot; i++){
		printf("%d---%lld----%d\n", e[i].u, e[i].dis, e[i].v);
	}
}
int main(){
	int t;
	scanf("%d", &t);
	while(t--){
		scanf("%d", &n);
		init();
		for(int i = 1; i <= n; i++){
			scanf("%d%d", &p[i].first, &p[i].second);
		}
		for(int i = 1; i <= n; i++){
			for(int j = i + 1; j <= n; j++){
				e[++tot] = (edge){i, j, (ll)1 * (p[i].first - p[j].first) * (p[i].first - p[j].first) + (ll)1 * (p[i].second - p[j].second) * (p[i].second - p[j].second)};
			}
		}
		sort(e + 1, e + tot + 1);
		//print();
		ll tmp = -1;
		for(int i = 1; i <= tot; i++){
			int u, v; u = e[i].u; v = e[i].v;
			if(tmp != e[i].dis){
				dfn++;
			}///新的点对距离
			if((!vis[u] || vis[u] == dfn) && (!vis[v] || vis[v] == dfn)){
				vis[u] = vis[v] = dfn;
			}
		}
		if(vis[1]){
			puts("YES");
		}
		else{
			puts("NO");
		}
	}
	return 0;
}

你可能感兴趣的:(2020年暑假集训)