CF1458E Nim Shortcuts

两堆石子的nim游戏,另外定义了(n)个点((x_i,y_i))为必败点。

一堆询问问((x,y))是否必胜。

(n,mle 10^5)


先判掉刚好在必败点的情况。

考虑转移式,((x,y))必胜当且仅当((x,y))正下或正左有必败点。

如果能求出每行每列的第一个必败点就好做了。

枚举行,算出在这行的被硬点的必败点的最小值,以及(mex({x|x列上方存在必败点}))。两者取(min),就是这行的第一个必败点。在这个必败点之后,除了被钦定的必败点之外都是必胜点。

由于行数比较多,对于空行(没有操作和询问的行)之间的必败点随便处理一下(就是移动(mex))。


using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#define N 100005
#define ll long long
#define INF 1000000001ll
int n,m;
set<ll> fail;
map<int,vector<int> > v;
int q[N];
set<int> c;
int ans[N];
int main(){
//	freopen("in.txt","r",stdin);
//	freopen("out.txt","w",stdout);
	scanf("%d%d",&n,&m);
	for (int i=1;i<=n;++i){
		int x,y;
		scanf("%d%d",&x,&y);
		fail.insert(x*INF+y);
		v[x].push_back(y);
	}
	fail.insert(0);
	v[0].push_back(0);
	for (int i=1;i<=m;++i){
		int x,y;
		scanf("%d%d",&x,&y);
		if (fail.find(x*INF+y)!=fail.end())
			ans[i]=0;
		else
			v[x].push_back(-i);
		q[i]=y;
	}
	int mex=0,lst=-1;
	for (auto p=v.begin();p!=v.end();lst=p->first,++p){
		for (int i=lst+1;i<p->first;/*++i*/){
			while (c.find(mex)!=c.end())
				mex++;
			auto q=c.upper_bound(mex);
			if (q==c.end()){
				mex+=p->first-i;
				break;
			}
			int tmp=min(*q-mex,p->first-i);
			mex+=tmp;
			i+=tmp;
		}
		int mn=INF;
		for (int i=0;i<p->second.size();++i)
			if (p->second[i]>=0)
				mn=min(mn,p->second[i]);
		while (c.find(mex)!=c.end())
			mex++;
		if (mex<mn)
			c.insert(mex);
		mn=min(mn,mex);
		for (int i=0;i<p->second.size();++i)
			if (p->second[i]>=0)
				c.insert(p->second[i]);
			else{
				int y=q[-p->second[i]];
				ans[-p->second[i]]=(y!=mn);
			}
	}
	for (int i=1;i<=m;++i)
		if (ans[i])
			printf("WIN
");
		else
			printf("LOSE
");
	return 0;
}
原文地址:https://www.cnblogs.com/jz-597/p/14164099.html