bzoj 4651 网格

4651: [Noi2016]网格

Time Limit: 50 Sec   Memory Limit: 1024 MB
Submit: 147   Solved: 107
[ Submit][ Status][ Discuss]

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 列的一只跳蚤替换为蛐蛐,从而使得存在两只跳蚤不连通
并且不存在更优的方案。
对于第三组数据,最初已经存在两只跳蚤不连通,故不需要再进行替换。
对于第四组数据,由于最多只有一只跳蚤,所以无论如何替换都不能存在两只跳蚤不连通

HINT

Source




【分析】

摔。破题抄一年。

http://blog.csdn.net/crzbulabula/article/details/54016812

自己写不对...苦恼.jpg



【代码】

//NOI 2016 网格 
#include<bits/stdc++.h>
#define LL long long
#define M(a) memset(a,0,sizeof a)
#define fo(i,j,k) for(int i=j;i<=k;i++)
using namespace std;
const int mxn=2500005;
bool flag;
int n,m,c,T,tot,tim,num,cnt;
bool vis[mxn],bo[mxn],yes[mxn];
int dx[9]={0,-1,0,0,1,-1,-1,1,1};
int dy[9]={0,0,1,-1,0,1,-1,1,-1};
int bel[mxn],dfn[mxn],low[mxn],ccz[mxn];
struct point
{
	int x,y;
	bool operator < (const point &b) const
	{
		if(x<b.x) return 1;
		if(x>b.x) return 0;
		return y<b.y;
	}
}p[mxn],q[mxn];
vector <LL> v0[mxn],v1[mxn],node[mxn];
inline LL P(int x,int y) {return (LL)x*m+y;}
map <point,int> m1,m2;
inline int read()
{
	int x=0;char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
	return x;
}
inline void Dfs(int u)
{
	bel[u]=num;
	for(int i=0;i<v0[u].size();i++)
	{
		int v=v0[u][i];
		if(bo[v]) continue;
		bo[v]=1,Dfs(v);
	}
}
inline void dfs(int u,int fa)
{
	int son=0;
	ccz[u]=cnt;
	dfn[u]=low[u]=(++tim);
	for(int i=0;i<v1[u].size();i++)
	{
		int v=v1[u][i];
		if(v==fa) continue;
		if(!dfn[v])
		{
			dfs(v,u),son++;
			low[u]=min(low[u],low[v]);
			if(dfn[u]<=low[v]) yes[u]=1;
		}
		else low[u]=min(low[u],dfn[v]);
	}
	if(fa==0 && son==1) yes[u]=0;
}
inline bool check(int u)
{
	int nx=q[u].x,ny=q[u].y;
	fo(j,1,8)
	{
		int ex=nx+dx[j],ey=ny+dy[j];
		point tmp=(point){ex,ey};
//		printf("(%d %d) (%d %d)\n",nx,ny,ex,ey);
		if(m1.count(tmp)) return 1;
	}
	return 0;
}
inline bool connect()
{
	fo(i,1,c)
	  fo(j,1,8)
	  {
	  	  point g=(point){p[i].x+dx[j],p[i].y+dy[j]};
	  	  if(m1.count(g)) v0[i].push_back(m1[g]);
	  }
	fo(i,1,c) if(!bo[i]) bo[i]=1,num++,Dfs(i);
	fo(i,1,c) fo(x,-2,2) fo(y,-2,2)
	{
		point g=(point){p[i].x+x,p[i].y+y};
		if(g.x<=0 || g.x>n || g.y<=0 || g.y>m) continue;
		if(!m1.count(g) && !m2.count(g))
		{
			q[++tot]=g,m2[g]=tot;
			node[bel[i]].push_back(tot);
		}
	}
	fo(i,1,tot) fo(j,1,4)
	{
		point g=(point){q[i].x+dx[j],q[i].y+dy[j]};
		if(m2.count(g)) v1[i].push_back(m2[g]);
	}
	fo(i,1,tot) if(!dfn[i]) cnt++,dfs(i,0);
	fo(i,1,tot) if(yes[i] && check(i)) flag=1;
	fo(i,1,num) for(int j=1;j<node[i].size();j++)
	{
		int now=node[i][j],pre=node[i][j-1];
		if(ccz[now]!=ccz[pre]) return 0;
	}
	return 1;
}
inline void clear()
{
	m1.clear(),m2.clear();
	fo(i,1,c) v0[i].clear(),bo[i]=bel[i]=0;
	fo(i,1,c) node[i].clear();
	fo(i,1,tot) dfn[i]=low[i]=yes[i]=0;
	fo(i,1,tot) v1[i].clear();
	tot=tim=num=cnt=flag=0;
}
int main()
{
	T=read();
	while(T--)
	{
		clear();
		n=read(),m=read(),c=read();
		fo(i,1,c)
		{
			p[i].x=read(),p[i].y=read();
			m1[p[i]]=i;
		}
		if((LL)n*m-c<=1) {puts("-1");continue;}
		if(!connect()) {puts("0");continue;}
		if((LL)n*m-c==2) {puts("-1");continue;}
		if(n==1 || m==1) flag=1;
		if(flag) puts("1");else puts("2");
	}
	return 0;
}
/*
1
1000000000 1000000000 2
1 999999999
2 1000000000
*/


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值