zoukankan      html  css  js  c++  java
  • 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=0\)\(y=0\) 的位置都是必胜态;\((1,1)\),因为不存在任何一个与其同行列的必败态,故也是必败态,然后所有其它 \(x=1\)\(y=1\) 的态都是必胜态……

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

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

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

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

    否则,即 \(z\leq y\),此时 \((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(n\log n)\)

    代码:

    #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;
    }
    

  • 相关阅读:
    除去DataTable中的空行!
    通过cmd命令安装、卸载、启动和停止Windows Service(InstallUtil.exe)
    installshield学习笔记
    托管与非托管
    C#语言之“String.Split”的使用【转】
    C#语言之“中英文混合字符串对齐”的方法
    栈和堆的区别
    UNICODE与ANSI的区别
    C#多线程:深入了解线程同步lock,Monitor,Mutex,同步事件和等待句柄(中)
    java自定义注解
  • 原文地址:https://www.cnblogs.com/Troverld/p/14605767.html
Copyright © 2011-2022 走看看