标签:continue name turn 时间复杂度 nim游戏 pre 一个 span begin
我们考虑把一对石子堆 \((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;
}
标签:continue name turn 时间复杂度 nim游戏 pre 一个 span begin
原文地址:https://www.cnblogs.com/Troverld/p/14605767.html