题意:有一个棋盘,棋盘上有一些棋子,两个人轮流拿棋,第一个人可以随意拿,以后每一个人拿走的棋子与上一个人拿走的棋子的曼哈顿距离不得超过$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. hdu 1520Anniversary party 树形dp入门

    There is going to be a party to celebrate the 80-th Anniversary of the Ural State University. The Un ...

  2. poj 2104 (划分树模板)

    Description You are working for Macrohard company in data structures department. After failing your ...

  3. jsp小知识点(2)

    一:自定义的函数库:在wh.tld中 <description>JSTL 1.1 functions library</description> <display-nam ...

  4. php2go - Go 实现 PHP 常用内置函数

    [转]http://www.syyong.com/Go/php2go-Use-Golang-to-implement-PHP-s-common-built-in-functions.html 使用Go ...

  5. 【BZOJ】1708: [Usaco2007 Oct]Money奶牛的硬币

    [算法]DP [题解] 如果每个排列算一种,则令f[i]表示凑成面值为i的方案数,容易推出f[i]+=f[i-a[j]]. 现在是每个组合才算一种,令f[i][j]第二维表示只使用前j种面值,f[i] ...

  6. [Leetcode Week8]Triangle

    Triangle 题解 原创文章,拒绝转载 题目来源:https://leetcode.com/problems/triangle/description/ Description Given a t ...

  7. kernelchina.org内核研究

    kernelchina.org 内核研究 转自:http://www.kernelchina.org

  8. twitter api取出的日期格式化

    import pickle import datetime crate_time_list=[] twitter_id_list=[] twitter_url_list=[] twitter_text ...

  9. C# WinForm窗体界面设置

    设置方法: 一:Form对象 属性: 设计中的Name:窗体类的类名AcceptButton:窗口的确定按钮CancelButton:窗口按ESC的取消按钮 1.外观 Backcolor:背景颜色Fo ...

  10. Selenium2+python自动化43-判断title(title_is)【转载】

    前言 获取页面title的方法可以直接用driver.title获取到,然后也可以把获取到的结果用做断言. 本篇介绍另外一种方法去判断页面title是否与期望结果一种,用到上一篇Selenium2+p ...