题意:有一个棋盘,棋盘上有一些棋子,两个人轮流拿棋,第一个人可以随意拿,以后每一个人拿走的棋子与上一个人拿走的棋子的曼哈顿距离不得超过$L$,无法拿棋的人输,问后手能否胜利

把棋子看做点,如果两个棋子的距离$\leq L$就连一条边,显然一局游戏只能在一个连通块里玩

如果某一个连通块只有一个点,那么先手拿走它,后手就输了

如果一个连通块有很多个点,做一次匹配,如果有完美匹配,那么后手胜利,否则先手胜利

为什么?

假设$A$先手,$B$后手,如果没有完美匹配,那么$A$可以选择一个非匹配点开始游戏,每次$B$都必须选择一个匹配点,然后$A$可以选择走匹配边,如此往复,最后一定是$A$走到某个地方使得$B$不能再走了(如果$B$还能走并且使得$A$不能走,说明这个图里还有增广路,不是最大匹配)

如果有完美匹配,$A$一开始只能选择匹配点,每次$B$可以走匹配边,如此往复,最后一定是$A$走不动了(如果$A$还能走并使得$B$走不动,那么最后到的是一个未匹配点,图就不是完美匹配了)

所以建出图之后直接跑带花树,判断是否存在完美匹配即可

#include<stdio.h>
#include<string.h>
int n,head,tail,h[400],nex[160000],to[160000],q[400],fa[400],type[400],match[400],pre[400],tm[400],M;
void swap(int&a,int&b){a^=b^=a^=b;}
void add(int a,int b){
	M++;
	to[M]=b;
	nex[M]=h[a];
	h[a]=M;
}
int get(int x){return(fa[x]==x)?x:(fa[x]=get(fa[x]));}
int lca(int x,int y){
	M++;
	while(1){
		if(x){
			x=get(x);
			if(tm[x]==M)return x;
			tm[x]=M;
			x=pre[match[x]];
		}
		swap(x,y);
	}
}
void blossom(int x,int y,int p){
	while(get(x)!=p){
		pre[x]=y;
		y=match[x];
		if(type[y]==2){
			type[y]=1;
			tail++;
			q[tail]=y;
		}
		if(fa[x]==x)fa[x]=p;
		if(fa[y]==y)fa[y]=p;
		x=pre[y];
	}
}
void bfs(int x){
	int i,now,las;
	memset(pre,0,sizeof(pre));
	memset(type,0,sizeof(type));
	for(i=1;i<=n;i++)fa[i]=i;
	type[x]=1;
	head=tail=1;
	q[1]=x;
	while(head<=tail){
		x=q[head];
		head++;
		for(i=h[x];i;i=nex[i]){
			if(type[to[i]]==2||get(x)==get(to[i]))continue;
			if(type[to[i]]==0){
				pre[to[i]]=x;
				type[to[i]]=2;
				if(match[to[i]]==0){
					now=to[i];
					while(now){
						las=match[pre[now]];
						match[now]=pre[now];
						match[pre[now]]=now;
						now=las;
					}
					return;
				}
				type[match[to[i]]]=1;
				tail++;
				q[tail]=match[to[i]];
			}else{
				now=lca(x,to[i]);
				blossom(x,to[i],now);
				blossom(to[i],x,now);
			}
		}
	}
}
int x[400],y[400];
int abs(int x){return x>0?x:-x;}
int dis(int i,int j){
	return abs(x[i]-x[j])+abs(y[i]-y[j]);
}
int main(){
	int i,j,L;
	while(~scanf("%d",&n)){
		memset(h,0,sizeof(h));
		memset(match,0,sizeof(match));
		memset(tm,0,sizeof(tm));
		M=0;
		for(i=1;i<=n;i++)scanf("%d%d",x+i,y+i);
		scanf("%d",&L);
		for(i=1;i<=n;i++){
			for(j=1;j<=n;j++){
				if(i!=j&&dis(i,j)<=L)add(i,j);
			}
		}
		M=0;
		for(i=1;i<=n;i++){
			if(match[i]==0)bfs(i);
		}
		M=1;
		for(i=1;i<=n;i++){
			if(match[i]==0){
				puts("NO");
				M=0;
				break;
			}
		}
		if(M)puts("YES");
	}
}

[ZOJ3316]Game的更多相关文章

  1. 【ZOJ3316】Game(带花树)

    [ZOJ3316]Game(带花树) 题面 Vjudge 翻译: 给定棋盘上\(n\)个旗子 一开始先手可以随便拿, 然后每次都不能取离上次的曼哈顿距离超过\(L\)的旗子 谁不能动谁输. 问后手能否 ...

  2. [ZOJ3316]:Game

    题面 vjudge Sol 有一个棋盘,棋盘上有一些棋子,两个人轮流拿棋,第一个人可以随意拿,以后每一个人拿走的棋子与上一个人拿走的棋子的曼哈顿距离不得超过L,无法拿棋的人输,问后手能否胜利 首先距离 ...

随机推荐

  1. Codeforces Round #328 (Div. 2) A

    A. PawnChess time limit per test 1 second memory limit per test 256 megabytes input standard input o ...

  2. c++编辑器下载地址

    https://msdn.itellyou.cn/ 输入上述地址选中下图所示的按钮:

  3. bzoj 2566 calc 拉格朗日插值

    calc Time Limit: 30 Sec  Memory Limit: 512 MBSubmit: 377  Solved: 226[Submit][Status][Discuss] Descr ...

  4. windows 系统下git 的使用

    前言: 最新版本的git for windows也是有界面的,不再是以前的纯命令行操作,但是我习惯了乌龟,所以感觉还是直接用乌龟比较方便点~~ 前提,已安装以下: git for windows,未安 ...

  5. IntelliJ 创建main函数快捷

    今天偶然发现了IntelliJ中 创建main函数的快捷键,依次还有for循环,System.out.println(); 在编写代码的时候直接输入psv就会看到一个psvm的提示,此时点击tab键一 ...

  6. 计算n阶行列式的模板

    之前在学习计数问题的时候也在网上找了很多关于行列式的资料 但是发现很多地方都只介绍2\3阶的情况 一些论文介绍的方法又看不懂 然后就一直耽搁着 今天恰好出到这样的题目 发现标算的代码简介明了 还挺开心 ...

  7. 扑克牌(cards)

    扑克牌 思路 这题也是二分!! 我们二分有几套牌,然后再去检验是否符合,至于怎么想到的,不要问我,我也不知道 那么我们主要解决的就是check函数 我们将二分的套数和每种牌的数量进行比较,如果该种牌的 ...

  8. 不能使用联机NuGet 程序包

    打开菜单栏>>工具>>选项>>NuGet 程序包管理器>>选择程序包源 勾选上nuget.org,即可使用联机的Nuget包  

  9. 如何在本机搭建SVN服务器【转】

    转自:http://www.cnblogs.com/loveclumsybaby/archive/2012/08/21/2649353.html 目的:在没有正式的SVN服务器的情况下,完成代码的本地 ...

  10. 简单粗暴的webapp语言国际化

    不同语言以json格式存放不同文件 { "information": "个人资料", "fuckworld":"你好世界" ...