1. 程式人生 > 其它 >【題解】CF1458E Nim Shortcuts

【題解】CF1458E Nim Shortcuts

比 D 題的陰間尤拉路徑好想多了(

首先如果 \(n=0\) 就是 nim 遊戲,當且僅當 \(x=y\) 時先手必敗。

我們定義先手必勝為 \(1\) ,先手必敗為 \(0\) ,那麼我們可以畫出一個 \(0/1\) 表格,格子 \((x,y)\) 上的數表示對應狀態先手的勝負。

如果 \(n=0\) ,那麼就是一個除了直線 \(x-y=0\) 上為 \(0\) ,其餘格子都是 \(1\) 的網格。

現在考慮 shortcut 操作。

不難發現,這等價於強行修改格子 \((x,y)\)\(0\)

根據博弈論的性質,如果當前狀態可以到達必敗態,則當前狀態為必勝態。

這相當於將第 \(x\) 行和第 \(y\)

列全部修改為 \(1\) ,除了格子 \((x,y)\)\(0\)。然後將原來的必敗直線 \(x-y=0\) 平移一個單位。

這等價於刪去一行或一列。如果 \(x<y\) 則刪除第 \(y\) 列,如果 \(x>y\) 則刪除第 \(x\) 行,如果 \(x=y\) ,這本來就是必敗態,不需要改變。

所以我們只用支援刪除行/列,和原來網格中的 \((a,b)\) 在刪除了這些行/列後的新位置 \((a,b)\)

這等價於查詢 \(\le a/b\) 的行/列中被刪除了多少個,經典二維數點問題,離線後用樹狀陣列維護。時間複雜度 \(\mathcal{O}(N\log N)\)

需要注意一些細節。需要特判 \((a,b)\)

是 shortcut 時是必敗態,當第 \(a\) 行或第 \(b\) 列被刪除時是必勝態

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 100005
using namespace std;
int n,m,o[N],b[N],T,c[N],ans[N],sz,v[N],cut = ~0;
inline void add(int x,int y){for(;x<=T;x+=x&-x)c[x]+=y;}
inline int ask(int x){int sum=0;for(;x;x-=x&-x)sum+=c[x];return sum;}
struct node{
	int op,a,b;
	bool operator<(const node o)const{
		if(a!=o.a)return a<o.a;
		return b<o.b;
	}
	bool operator<=(const node o)const{
		if(a!=o.a)return a<o.a;
		return b<=o.b;
	}
}u[N],q[N];
set<pair<int,int> >s;
void ins(int x,int y){
	int w = lower_bound(b+1,b+T+1,y) - b;
	int cur = sz - ask(w);
	if(x - y < cur){  // x - sz < y - ask()
		if(!v[w])v[w] = 1,add(w, 1);
	}
	else if(x - y > cur){
		if(x != cut) cut = x , sz++;
	}s.insert(make_pair(x,y));
}
int calc(int x,int y){
	if(s.find(make_pair(x,y)) != s.end())return 0;
	int w = upper_bound(b+1,b+T+1,y) - b - 1;
	if(v[w] && b[w] == y)return 1;
	if(cut == x)return 1;
	int cur = sz - ask(w);
	//cout<<"ss "<<x<<" "<<y<<" "<<sz<<" "<<ask(w)<<endl;
	if(x - y == cur)return 0;
	return 1;
}
int main(){
	//freopen("INPUT","r",stdin);
	scanf("%d%d",&n,&m);
	rep(i,1,n)scanf("%d%d",&u[i].a,&u[i].b),o[i]=u[i].b;
	rep(i,1,m)scanf("%d%d",&q[i].a,&q[i].b),q[i].op=i;
	sort(o+1,o+n+1);rep(i,1,n)if(i==1||o[i]!=o[i-1])b[++T]=o[i];
	sort(u+1,u+n+1);sort(q+1,q+m+1);int j=1;
	rep(i,1,m){
		while(j<=n&&u[j]<=q[i])ins(u[j].a,u[j].b),j++;
		ans[q[i].op]=calc(q[i].a,q[i].b);
	}
	rep(i,1,m)if(ans[i])puts("WIN");else puts("LOSE");
	return 0;
}