CF1458E Nim Shortcuts

I.CF1458E Nim Shortcuts

我们考虑把一对石子堆 (x,y) 映射到笛卡尔平面上的一个点 (x,y)

先考虑没有捷径时的方案。很明显,这是简单的NIM游戏,当且仅当直线 y=x 上的状态是先手必败态。但是,我们有必要搞清楚该结论的由来:

如果对于一个位置 (x,y),不存在任何一个可以由它一步走到的必败态,则它就是必败态;否则就是必胜态。在NIM游戏中,位置 (x,y) 可以走到所有的 (i,y)(x,j),其中 i<x,j<y

首先,位置 (0,0) 肯定是必败态;于是由上文结论,所有其它 x=0y=0 的位置都是必胜态;(1,1),因为不存在任何一个与其同行列的必败态,故也是必败态,然后所有其它 x=1y=1 的态都是必胜态……

我们仍然考虑上述分析,只不过现在有了“捷径”。

考虑当前已经分析到了位置 (x,y)。显然,如果不存在任何一个与它同行列的“捷径”,则有无捷径并无影响,直接把 (x,y) 看作必败态,然后继续去分析 (x+1,y+1)

否则,存在一个与它同行列的“捷径”。这里先分析有同列的情况(即有相同的 x 值)。设该捷径为 (x,z)

明显,若 z>y,捷径 (x,z) 并不可能从位置 (x,y) 走到,也就无从对其产生影响了,于是就和上文一样处理即可。

否则,即 zy,此时 (x,y) 可以走到 (x,z)(当然,二者可能重合——但是此时因为 (x,z) 会在行上和列上同时被看作可以走到的位置,所以不会产生影响),因为 (x,z) 是必败态,所以此时 (x,y) 就成了必胜态。因为在 y 这一行上还没有必败态,但是 x 这一列上已经有必败态了,所以把 (x,y) 看作必胜态,然后分析 (x+1,y)

存在一个同行(相同的 y)的捷径的情况也类似,此时接下来要分析的是 (x,y+1)。当然,两个也可能同时发生——此时应考虑 (x+1,y+1)

初始直接开始分析位置 (0,0)

明显,我们只需要考虑关键的行和列即可,剩下的位置直接按照一条斜率为 1 的线段进行处理即可。询问的时候先判断其本身是否是一个“捷径”,然后再判断其是否在一条上述线段上。使用 set 之类加以维护,时间复杂度 O(nlogn)

代码:

#include<bits/stdc++.h>
using namespace std;
int n,m,x,y;
map<pair<int,int>,bool>mp;
map<int,int>X,Y;
set<tuple<int,int,int> >s;
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d%d",&x,&y),mp[make_pair(x,y)]=true;
		if(X.find(x)==X.end())X[x]=y;else X[x]=min(X[x],y);
		if(Y.find(y)==Y.end())Y[y]=x;else Y[y]=min(Y[y],x);
	}
	x=y=0;
	for(auto i=X.begin(),j=Y.begin();i!=X.end()||j!=Y.end();){
		while(i!=X.end()&&i->first<x)i++;
		while(j!=Y.end()&&j->first<y)j++;
		int xx=0x3f3f3f3f,yy=0x3f3f3f3f;
		if(i!=X.end())xx=i->first;
		if(j!=Y.end())yy=j->first;
		int mn=min(xx-x,yy-y);
		bool xxx=false,yyy=false;
		if(x+mn==xx&&i->second<=y+mn)xxx=true;
		if(y+mn==yy&&j->second<=x+mn)yyy=true;
//		printf("(%d,%d)->(%d,%d):%d %d|%d,%d\n",x,y,x+mn,y+mn,xxx,yyy,xx,yy);
		if(mn+1-max(xxx,yyy))s.insert(make_tuple(x+mn-max(xxx,yyy),x,y));
		if(!max(xxx,yyy))xxx=yyy=true;
		x+=mn+xxx,y+=mn+yyy;
	}
	s.insert(make_tuple(0x3f3f3f3f,x,y));
//	for(auto i:s)printf("(%d,%d)(%d,%d)\n",get<1>(i),get<2>(i),get<0>(i),get<2>(i)+get<0>(i)-get<1>(i));
	for(int i=1;i<=m;i++){
		scanf("%d%d",&x,&y);
		if(mp.find(make_pair(x,y))!=mp.end()){puts("LOSE");continue;}
		auto tmp=s.lower_bound(make_tuple(x,0,0));
		if(tmp==s.end()||get<1>(*tmp)>x){puts("WIN");continue;}
		puts(get<2>(*tmp)+x-get<1>(*tmp)==y?"LOSE":"WIN");
	}
	return 0;
}

posted @   Troverld  阅读(74)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?
点击右上角即可分享
微信分享提示