BZOJ4651 [Noi2016]网格/UOJ220

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作。

本文作者:ljh2000
作者博客:http://www.cnblogs.com/ljh2000-jump/
转载请注明出处,侵权必究,保留最终解释权!

Description

跳蚤国王和蛐蛐国王在玩一个游戏。

他们在一个 n 行 m 列的网格上排兵布阵。其中的 c 个格子中 (0≤c≤nm),每个格子有一只蛐蛐,其余的格子中,每个格子有一只跳蚤。

我们称占据的格子有公共边的两只跳蚤是相邻的。

我们称两只跳蚤是连通的,当且仅当这两只跳蚤相邻,或存在另一只跳蚤与这两只跳蚤都连通。

现在,蛐蛐国王希望,将某些(0 个,1 个或多个)跳蚤替换成蛐蛐,使得在此之后存在至少两只跳蚤不连通。

例如:我们用图表示一只跳蚤,用图表示一只蛐蛐,那么图 1 描述了一个 n=4,m=4,c=2的情况。

这种情况下蛐蛐国王可以通过将第 2 行第 2 列,和第 3 行第 3 列的两只跳蚤替换为蛐蛐,从而达成他的希望,如图 2 所示。并且,不存在更优的方案,但是可能存在其他替换 2 只跳蚤的方案。

你需要首先判断蛐蛐国王的希望能否被达成。如果能够达成,你还需要最小化被替换的跳蚤的个数。

 

Input

每个输入文件包含多组数据。

输入文件的第一行只有一个整数 TT,表示数据的组数。保证 1≤T≤20。

接下来依次输入 TT 组数据,每组数据的第一行包含三个整数 n, m, c。

保证1≤n,m≤10^9,0≤c≤min(nm,105)

接下来 c行,每行包含两个整数 x, y表示第 x 行,第 y 列的格子被一个蛐蛐占据(1≤x≤n,1≤y≤m)每一组数据当中,同一个蛐蛐不会被多次描述。

同一行相邻的整数之间由一个空格隔开。

1≤n,m≤10^9, 0≤c≤nm, 1≤x≤n, 1≤y≤m

1≤T≤20。我们记 ∑c为某个测试点中,其 T 组输入数据的所有 c 的总和,∑c≤10^5

Output

对于每一组数据依次输出一行答案。

如果这组数据中,蛐蛐国王的希望不能被达成,输出-1。否则,输出被替换的跳蚤的个数的最小值

Sample Input

4

4 4 2

1 1

4 4

2 3 1

1 2

2 2 2

1 1

2 2

1 1 0

Sample Output

2

1

0

-1

explanation

第一组数据就是问题描述中的例子。

对于第二组数据,可以将第 2 行第 2 列的一只跳蚤替换为蛐蛐,从而使得存在两只跳蚤不连通

并且不存在更优的方案。

对于第三组数据,最初已经存在两只跳蚤不连通,故不需要再进行替换。

对于第四组数据,由于最多只有一只跳蚤,所以无论如何替换都不能存在两只跳蚤不连通

正解:hash+tarjan求割点

解题报告:

  这道题我当时在考场上很快反应过来答案只有可能是0、1、2、-1…

  部分分我在讲题的时候已经详细说了,所以直接上正解好了嘿嘿嘿...

  考虑如果有大量空行则显然是无用的,具体思考后发现,对于一个蛐蛐,只有上下两行,左右两列是有用的,深入思考发现,我们只需要考虑每个蛐蛐的周围24个格子即可,直接相邻的叫做一级空地,和一级空地相邻的叫二级空地。

  官方题解给出了详细证明,不赘述其正确性,下面只讲做法:

  无解的情况可以很容易处理:假设跳蚤数量<=1或者只有2个跳蚤且相邻则无解;

  然后我们对于每个蛐蛐控制的范围,扣出所有的空地,按四联通建图,

  0个的情况是当把所有的连通块弄出来之后,假设一个蛐蛐控制的范围内的点,有不连通的空地,则说明已经隔断了;

  1的情况考虑本质是求出有无割点,对建出来的图中每个连通块跑tarjan,假如存在割点则答案是1;

  上述情况均不满足,答案就是2。

  但是很遗憾的是这么做只有60分,我WA了很久之后发现,割点只有在一级空地时才有效,在二级空地的情况是非法的!

  加入这条判断之后就AC了...

  这道题的细节比较多,似乎我为了方便写的是函数化形式,有点丑,而且代码长...

  讲道理这道题只要概括出模型,算法还是很simple的,就是要想到不断地优化来降低图的大小。

//It is made by ljh2000
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
const int MOD = 400007;
const int MAXN = 200011;
const int Bas = 1000000007;
const int MAXM = 5000011;
int n,m,c,yi[12][2]={{1,0},{0,1},{-1,0},{0,-1}};
int father[MAXM],ans,top;
LL dui[MAXM];
struct Cricket{ int x,y; }b[MAXN];
struct Hash{
	int ecnt,first[MOD+12],next[MAXM];
	LL to[MAXM];
	inline void init(){ ecnt=0;	memset(first,0,sizeof(first)); }
	inline void insert(LL x,LL y){
		LL num=(x*Bas+y); int cc=(int)(num%MOD);
		next[++ecnt]=first[cc]; first[cc]=ecnt; to[ecnt]=num;
	}

	inline int query(LL x,LL y){
		LL num=(x*Bas+y); int cc=(int)(num%MOD);
		for(int i=first[cc];i;i=next[i]) if(to[i]==num) return i;
		return -1;
	}
}mp1,mp2;

inline int getint(){
    int w=0,q=0; char c=getchar(); while((c<‘0‘||c>‘9‘) && c!=‘-‘) c=getchar();
    if(c==‘-‘) q=1,c=getchar(); while (c>=‘0‘&&c<=‘9‘) w=w*10+c-‘0‘,c=getchar(); return q?-w:w;
}

namespace Graph{
	const int maxn = 2400011;
	const int maxm = 10000011;
	int ecnt,first[maxn],to[maxm],next[maxm],dfn[maxn],low[maxn],cnt,kcnt,stack[maxn],stack_top;
	bool iscut[maxn],yiji[maxn];
	inline void init(){	ecnt=0; memset(first,0,sizeof(first)); memset(yiji,0,sizeof(yiji)); }
	inline int find(int x){ if(father[x]!=x) father[x]=find(father[x]); return father[x]; }
	/*inline int find(int x){
		stack_top=0;
		while(father[x]!=x) {
			stack[++stack_top]=x;
			x=father[x];
		}
		while(stack_top>0) {
			father[stack[stack_top]]=x;
			stack_top--;
		}
		return x;
	}*/
	inline void link(int x,int y){
		next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y;
		next[++ecnt]=first[y]; first[y]=ecnt; to[ecnt]=x;
	}
	inline void tarjan(int x,int fa){
		dfn[x]=low[x]=++cnt; int size=0;
		for(int i=first[x];i;i=next[i]) {
			int v=to[i]; if(v==fa) continue;
			if(dfn[v]==-1) {
				size++;	tarjan(v,x);
				low[x]=min(low[x],low[v]);
				if(low[v]>=dfn[x]) iscut[x]=1;
			}
			else if(low[v]<low[x]) low[x]=low[v];
		}
		if(fa==-1 && size==1) iscut[x]=0;
	}
	inline void work(){
		if((LL)n*m-c==2 && mp2.ecnt==2) { if(to[first[1]]==2) { puts("-1"); return ; } }//特判只有相邻的两个的情况
		for(int i=1;i<=mp2.ecnt;i++) dfn[i]=low[i]=-1,iscut[i]=0; cnt=0; kcnt=0;
		for(int i=1;i<=mp2.ecnt;i++) if(dfn[i]==-1) { kcnt++,tarjan(i,-1); }
		if(n==1 || m==1) { puts("1"); return ; }//特判n、m等于1的情况
		for(int i=1;i<=mp2.ecnt;i++) if(yiji[i] && iscut[i]) { puts("1"); return ; }
		puts("2");
	}
}

inline void build(int inx,int iny){
	int nowx,nowy,nx,ny,nn;
	for(int i=-2;i<=2;i++)
		for(int j=-2;j<=2;j++) {
			if(i==0 && j==0) continue;
			nowx=inx+i; nowy=iny+j; if(nowx<=0 || nowy<=0 || nowx>n || nowy>m) continue;
			if(mp1.query(nowx,nowy)!=-1) continue; nn=mp2.query(nowx,nowy);
			if(nn!=-1) { if(abs(i)<=1 && abs(j)<=1) Graph::yiji[nn]=1;  continue; }
			mp2.insert(nowx,nowy); nn=mp2.query(nowx,nowy);
			if(abs(i)<=1 && abs(j)<=1) Graph::yiji[nn]=1;
			for(int l=0;l<4;l++) {
				nx=nowx+yi[l][0]; ny=nowy+yi[l][1]; if(nx<=0 || ny<=0 || nx>n || ny>m) continue;
				if(mp1.query(nx,ny)!=-1) continue; if(mp2.query(nx,ny)==-1) continue;
				Graph::link(nn,mp2.query(nx,ny));
			}
		}
}

inline void check_init(){
	using namespace Graph;
	int r1,r2; for(int i=1;i<=mp2.ecnt;i++) father[i]=i;
	for(int i=1;i<=mp2.ecnt;i++)
		for(int j=first[i];j;j=next[j]) {
			if(to[j]<=i) continue;
			r1=find(i); r2=find(to[j]);
			if(r1!=r2) father[r1]=r2;
		}
}

inline void check(int inx,int iny){
	using namespace Graph;
	int nowx,nowy; top=0;
	for(int i=-2;i<=2;i++)
		for(int j=-2;j<=2;j++){
			if(i==0 && j==0) continue;
			nowx=inx+i; nowy=iny+j; if(nowx<=0 || nowy<=0 || nowx>n || nowy>m) continue;
			if(mp1.query(nowx,nowy)!=-1) continue;
			dui[++top]=mp2.query(nowx,nowy);
		}
	for(int i=2;i<=top;i++) if(find(dui[i])!=find(dui[1])) { ans=0; return ; }
}

inline void work(){
	int T=getint();
	while(T--) {
		n=getint(); m=getint(); c=getint();
		for(int i=1;i<=c;i++) b[i].x=getint(),b[i].y=getint();
		if((LL)n*m-c<=1) { puts("-1"); continue; }//特判无解
		if(c==0 && n*m==2) { puts("-1"); continue; }//特判只有相邻的两个的情况
		mp1.init(); mp2.init(); Graph::init(); ans=-1;
		for(int i=1;i<=c;i++) mp1.insert(b[i].x,b[i].y);
		for(int i=1;i<=c;i++) build(b[i].x,b[i].y);
		//检查0的情况
		check_init();
		for(int i=1;i<=c;i++) { check(b[i].x,b[i].y); if(ans==0) break; }
		if(ans==0) { puts("0"); continue; }
		Graph::work();
	}
}

int main()
{
    work();
    return 0;
}

  

  

时间: 2024-10-30 03:23:38

BZOJ4651 [Noi2016]网格/UOJ220的相关文章

[UOJ#220][BZOJ4651][Noi2016]网格

试题描述 跳蚤国王和蛐蛐国王在玩一个游戏. 他们在一个 n 行 m 列的网格上排兵布阵.其中的 c 个格子中 (0≤c≤nm),每个格子有一只蛐蛐,其余的格子中,每个格子有一只跳蚤. 我们称占据的格子有公共边的两只跳蚤是相邻的. 我们称两只跳蚤是连通的,当且仅当这两只跳蚤相邻,或存在另一只跳蚤与这两只跳蚤都连通. 现在,蛐蛐国王希望,将某些(0 个,1 个或多个)跳蚤替换成蛐蛐,使得在此之后存在至少两只跳蚤不连通. 例如:我们用图表示一只跳蚤,用图表示一只蛐蛐,那么图 1 描述了一个 n=4,m

[bzoj4651]网格

考虑将最上中最左的跳蚤孤立,容易发现他的上面和左面没有跳蚤,因此只需要将他的右边和下边替换掉就可以了答案为-1有两种情况:1.c>=n*m-1;2.c=n*m-2且这两只跳蚤相邻对于其他情况,将所有跳蚤建图后判断:1.是否有多个连通块:2.是否有割点即可,由于跳蚤数量很多,容易发现只需要选择周围5*5的范围内有蛐蛐的跳蚤建图即可只选择3*3会有反例(以下q表示蛐蛐,t表示跳蚤):ttttttttqtttttttqtttttttt(注意如果c=0那么答案为((n>1)&&(m&g

NOI2016 高中OI生涯的最后一站

你乘坐的航班XXX已经抵达终点站——四川绵阳. “呼——”机舱外的天空灰沉沉的,不禁有些压抑与紧张. 一出机场,就看见南山中学的牌子,黄色衣服的志愿者们,还有热情的老师们. 感觉刚才的情绪又一扫而空了,转而迎来的是一种兴奋与激动. 学长和教练都曾说过:就当做一次展现自己实力的机会.从来不要给自己太大压力. 这样的话大多也埋在心里了吧,潜移默化的影响着自己的心情. 那就开心的去面对这几场考试好了. 首先领好东西,还去签名版上签了个名字,发现湖南参赛的选手果然好多呀...不过不知道今年能不能翻身成强

CSS3 网格布局(grid-layout)基础知识 - 网格模板属性(grid-template)使用说明

CSS3引入了新的网格布局(grid layout),以适应显示和设计技术的发展(尤其是移动设备优先的响应式设计). 主要目标是建立一个稳定可预料且语义正确的网页布局模式,用来替代过往表现不稳定且繁琐的table.flow以及JS脚本混合技术来实现的网页动态布局. 本文将简单而准确的介绍网格布局属性的基本概念和使用方法(摘自踏得网在线HTML5教程). 1. 概述 网格模板区域(grid-template-areas).网格模板行(grid-template-rows)和网格模板列(grid-t

在SOUI中使用网格布局

在实现网格布局前,SOUI支持两种布局形式:相对布局,和线性布局,其中线性布局是2017年2月份才支持的布局. 这两年工作都在Android这里,Android里有号称5大布局(RelativeLayout, LinearLayout, FrameLayout, GridLayout,TableLayout). FrameLayout很简单,在SOUI里一般用TabCtrl就实现了.RelativeLayout和SOUI自己的相对布局功能类似,线性布局也有了,但是一直没有实现GridLayout

任务八:响应式网格(栅格化)布局

任务目的 使用 HTML 与 CSS 实现类似 BootStrap 的响应式 12 栏网格布局,根据屏幕宽度,元素占的栏数不同. 任务描述 需要实现如 效果图 所示,调整浏览器宽度查看响应式效果,效果图中的红色的文字是说明,不需要写在 HTML 中. 任务注意事项 网格布局的作用在于更有效地控制元素在网页中所占比例的大小.比如,博客中有一个留言板模块,在比较大的屏幕上,我们希望它占了右边 25% 的宽度,在手机等比较小的屏幕上,我们希望它占 100% 的宽度,出现在博客文章下方.网格布局是一种实

bzoj2006[NOI2016]超级钢琴

bzoj2006[NOI2016]超级钢琴 题意: 超级钢琴可以弹奏出n个音符,编号为1至n.第i个音符的美妙度为Ai,其中Ai可正可负.一个“超级和弦”由若干个编号连续的音符组成,包含的音符个数不少于L且不多于R,其美妙度为包含的所有音符的美妙度之和.两个超级和弦被认为是相同的,当且仅当这两个超级和弦所包含的音符集合是相同的.现需创作一首由k个不同的超级和弦组成的乐曲.定义一首乐曲的美妙度为其所包含的所有超级和弦的美妙度之和.求能够创作出来的乐曲美妙度最大值是多少. 题解: 乍一看很难,实际上

[Noi2016十连测第三场]线段树

1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <algorithm> 5 #include <cmath> 6 using namespace std; 7 #define maxn 100005 8 #define maxk 4000005 9 int n,m,q,tot,t1,t2,ans,L[maxn][18],R[maxn][18]; 1

GridView网格控件

一.GridView控件用于显示一个网格图像, GridView主要是用在一些相册的布局显示图片.GridView采用的是二维表的方式显示单元格,就需要设置二维表的行和列.设置GridView的列可以使用<GridView>标签的columnWidth属性.也可以使用GridView类的setColumnWidth方法来设置列数,GridView中的单元格会根据列数自动拆行显示,因此不需要设置GridView的行数,但是需要设置android:numColumns属性.否则GridView只会