P1173 [NOI2016] グリッド

トピックプロバイダーLuogu

难度NOI/NOI+/CTSC

履歴スコア100

トピックの説明

ノミ王とクリケット王がゲームをしています。

これらは n 行 m 列のグリッド上に配置されます。cc グリッド (0 ≤ c ≤ n⋅m) のうち、各グリッドにはコオロギがあり、残りのグリッドには各グリッドにノミがいます。

共通のエッジを持つグリッドを占める 2 つのノミが隣接しているといいます。

2 匹のノミが隣接している場合、または両方のノミにつながっている別のノミがいる場合にのみ、2 匹のノミがつながっていると言います。

さて、コオロギ王は、一部のノミ (ゼロ、1 匹、またはそれ以上) がコオロギに置き換えられ、その後少なくとも 2 匹のノミが切り離されることを望んでいます。[写真]

例: 図 1 は、n=4、m=4、c=2 の場合を示しています。

この場合、右図のように、2行2列目と3行3列目の2匹のノミをコオロギに置き換えることで、キングコオロギの願いは達成されます。また、これより良い解決策はありませんが、2 匹のノミに代わる他の解決策があるかもしれません。

まずはクリケット王の願いがかなえるかどうかを判断する必要がある。それが達成できれば、交換されるノミの数も最小限に抑える必要があります。

入力フォーマット

各入力ファイルには複数のデータセットが含まれています。

入力ファイルの最初の行には、データのグループの数を示す整数 T が 1 つだけあります。

次に、TT セットのデータを順番に入力します。各データセットの最初の行には 3 つの整数 n、m、c が含まれます。

次の c 行目、各行には 2 つの整数 x、y が含まれており、x 行目、y 列目のグリッドがコオロギによって占められていることを意味します。各データ内で、同じコオロギが複数回記述されることはありません。

出力フォーマット

データのセットごとに、回答の行を順番に出力します。

このデータセットでクリケット王の願いがかなわない場合は、-1 を出力します。それ以外の場合は、交換されたノミの最小数を出力します。

入力サンプルと出力サンプル

入力#1

4
4 4 2
1 1
4 4
2 3 1
1 2
2 2 2
1 1
2 2
1 1 0

出力 #1

2
1
0
-1

 

指示/ヒント

サンプル説明

最初のデータセットは、問題の説明の例です。

2 番目のデータ セットでは、2 行目、2 列目の 1 匹のノミをコオロギに置き換えることができるため、接続されていない 2 匹のノミが存在し、これより良い解決策はありません。

3 番目のデータセットでは、2 匹のノミが最初に切断されているため、交換する必要はありません。

4 番目のデータでは、ノミは最大でも 1 匹しかいないため、どのように置き換えても 2 匹のノミが切断されることはありません。

データ範囲

すべてのテスト ポイントについて、1≤T≤20 であることが保証されます。∑c を、特定のテスト ポイントにおける入力データの T セットのすべての c の合計として記録します。すべてのテスト ポイントで、Σc≤105。

すべてのデータにおいて、1≦n、m≦10^9、0≦c≦n×m、1≦x≦n、1≦y≦mを満たします。

各テストポイントの詳細なデータ範囲を以下の表に示します。表内の n、m、および c はすべて単一の入力データ (テスト ポイントではない) に対するものであり、つまり、同じテスト ポイントにあるデータの T グループがすべて制限条件を満たします。また、∑c は単一のテストポイントが話されます。読みやすいように、「テスト ポイント」列は表の左側ではなく中央に配置されています。

 問題解決のアイデア:

 コオロギがいるものは黒い斑点、ノミがいるものは白い斑点と見なされます。

明らかに、答えは無解か 0、1、2 のみです。

  • 4 つの接続された白セルで構成されるグラフがすべて接続されていない場合、答えは 0 になります。

  • それ以外の場合、グラフにカットポイントがある場合、答えは 1 です。

  • それ以外の場合、グラフに 2 つ以下の点しかない場合、解決策はありません。

  • それ以外の場合 2

最初の 3 つのケースは明らかで、最後のケースはそれぞれ 33 点と 33 点を超える点を持つサブグラフである可能性があります。

したがって、マップを直接構築して tarjan を実行すると、O(nm) になる可能性があります。

ただし、このグラフには点は多くありますが、空いた部分はほとんどありません。縮小後も答えが変わらないように、点と辺が O(c) 個のグラフに縮小することを検討してください。

次の白い点のみを保持することを検討してください。

  • グリッドの 4 つの隅の少なくとも 1 つの x、yx、y 座標との差 \leq2≤2

  • とある黒い点8と繋がる

  • グリッドの上部または下部に、列内に少なくとも 1 つの黒い点がある

  • グリッドの左側または右側に、行内に少なくとも 1 つの黒い点がある

次に、残りのすべての白い点について、2 つの白い点が同じ行または列にあり、中央に他の点 (黒い点と残りの白い点を含む) がない場合は、片側を接続します。

接続された 4 つのエッジだけが構築されるわけではないことに注意してください。

たとえば、青いグリッドは予約ポイントです。

すると、この絵の答えは元の絵の答えとほとんどの場合同じですが、-1を判定する場合、絵内に2点あるがその2点にエッジがある場合は特別に判定する必要があることが分かりました。この場合、実際には少なくとも 3 つの点が存在し、答えは 1 になるか、n=1、m=1 と判断できます。

それが正しいかどうかはわかりません、また、正しい場合、どうやって証明すればよいでしょうか? とにかく、uoj のハック データがすべて揃ったので、ハッキングを続けることを歓迎します。

そして、各黒い点が周囲に最大 8 ポイント、側面に 4 ポイント、合計 12 ポイントしか寄与しないことを考慮すると、合計は O(c) ポイントになります。

したがって、複雑さのボトルネックはマップの構築、つまり複雑さ Θ(clogc) にあります。

標準コード:

#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
const int dx[4]={0,1,0,-1};
const int dy[4]={1,0,-1,0};
const int P=1000117;
const int N=100050;
char rB[1<<21],*rS,*rT;
inline char gc(){return rS==rT&&(rT=(rS=rB)+fread(rB,1,1<<21,stdin),rS==rT)?EOF:*rS++;}
inline int rd(){
    char c=gc();
    while(c<48||c>57)c=gc();
    int x=c&15;
    for(c=gc();c>=48&&c<=57;c=gc())x=(x<<3)+(x<<1)+(c&15);
    return x;
}
int x[N],y[N],G[N*24],to[N*192],nxt[N*192],sz,cnt,pre[N*24],dfsc,n,m,c,tmpx[N*24],tmpy[N*24],ctmp;
bool isok[N*24],iscut[N*24];
struct node{
	int x,y;
	node(){}
	node(int x,int y):x(x),y(y){}
};
queue<node> Q,q;
struct Hash{  //用hash实现map
	int h[P],vx[N*25],vy[N*25],p[N*25],nxt[N*25],sz;
	inline void clear(){
		memset(h,0,sizeof(h));sz=0;
	}
	inline void ins(int x,int y,int id){
		int pos=((ll)(x-1)*n+y-1)%P;
		vx[++sz]=x;vy[sz]=y;p[sz]=id;nxt[sz]=h[pos];h[pos]=sz;
	}
	inline int ask(int x,int y){
		for(int k=h[((ll)(x-1)*n+y-1)%P];k;k=nxt[k])if(vx[k]==x&&vy[k]==y)return p[k];
		return 0;
	}
}h,col,tem;
inline int Abs(int x){return x<0?-x:x;}
inline int Max(int a,int b){return a>b?a:b;}
inline void add(int u,int v){
	to[++sz]=v;nxt[sz]=G[u];G[u]=sz;
	to[++sz]=u;nxt[sz]=G[v];G[v]=sz;
}
inline bool check(){
	int i,j,k,tx,ty;
	for(i=1;i<=n;++i)
		for(j=1;j<=m;++j)if(!h.ask(i,j)){
			for(k=0;k<4;++k)if((tx=i+dx[k])&&tx<=n&&(ty=j+dy[k])&&ty<=m&&!h.ask(tx,ty))return 1;
			return 0;
		}
}
inline void bfs(int sx,int sy,int cl){  //第一次floodfill
	int i,u,v,tx,ty;
	q.push(node(sx,sy));col.ins(sx,sy,cl);
	while(!q.empty()){
		u=q.front().x;v=q.front().y;q.pop();
		for(i=0;i<4;++i)if((tx=u+dx[i])&&tx<=n&&(ty=v+dy[i])&&ty<=m&&h.ask(tx,ty)>0&&!col.ask(tx,ty)){
			col.ins(tx,ty,cl);  //用col来记录所属联通块编号(也成为颜色)
			q.push(node(tx,ty));
		}
	}
}
inline bool bfs2(int sx,int sy){
	int i,u,v,x,y,t;
	q.push(node(sx,sy));tem.ins(sx,sy,-1);
	while(!q.empty()){
		u=q.front().x;v=q.front().y;q.pop();
		for(x=Max(1,u-1);x<=n&&x<=u+1;++x)
			for(y=Max(1,v-1);y<=m&&y<=v+1;++y)if((t=h.ask(x,y))&&!tem.ask(x,y))if(t==-1){
				tem.ins(x,y,-1);  //用tem来防止对障碍结点重复访问
//对跳蚤结点的重复访问最多总共c*8个,不会影响复杂度
				q.push(node(x,y));
			}else{tmpx[++ctmp]=x;tmpy[ctmp]=y;}
	}
	if(ctmp==-1)return 1;
	for(i=1,t=col.ask(tmpx[0],tmpy[0]);i<=ctmp;++i)if(col.ask(tmpx[i],tmpy[i])!=t)return 0;
	return 1;
}
inline bool ncon(){  //判断是否不连通
	int i,u,v,ccl=0;
	col.clear();
	while(!Q.empty()){
		u=Q.front().x;v=Q.front().y;Q.pop();
		if(col.ask(u,v))continue;
		bfs(u,v,++ccl);
	}
	tem.clear();
	for(i=0;i<c;++i)if(!tem.ask(x[i],y[i])){
		ctmp=-1;
		if(!bfs2(x[i],y[i]))return 1;
	}
	return 0;
}
int dfs(int u,int fa){  //dfs求割顶
	int i,v,lowu=pre[u]=++dfsc,lowv,chd=0;
	for(i=G[u];i;i=nxt[i])if((v=to[i])!=fa)if(!pre[v]){
		++chd;
		if((lowv=dfs(v,u))>=pre[u])iscut[u]=1;
		if(lowv<lowu)lowu=lowv;
	}else if(pre[v]<lowu)lowu=pre[v];
	if(!fa&&chd==1)iscut[u]=0;
	return lowu;
}
int main(){
	int T=rd(),i,j,k,l,t,tt,tx,ty;
	bool ok;
	while(T--){
		n=rd();m=rd();c=rd();
		h.clear();
		for(i=0;i<c;++i){
			x[i]=rd();y[i]=rd();
			h.ins(x[i],y[i],-1);
		}
		if((ll)n*m-c<2ll){
			puts("-1");
			continue;
		}
		if((ll)n*m-c==2ll){
			puts(check()?"-1":"0");
			continue;
		}
		memset(G,0,sizeof(G));ok=sz=cnt=dfsc=0;
		memset(pre,0,sizeof(pre));
		memset(iscut,0,sizeof(iscut));
		memset(isok,0,sizeof(isok));
        //建图
		for(i=0;i<c;++i)
			for(j=Max(1,x[i]-2);j<=x[i]+2&&j<=n;++j)
				for(k=Max(1,y[i]-2);k<=y[i]+2&&k<=m;++k)if(!(t=h.ask(j,k))){
					h.ins(j,k,++cnt);Q.push(node(j,k));
					isok[cnt]=Max(Abs(j-x[i]),Abs(k-y[i]))<=1;
					for(l=0;l<4;++l)if((tx=j+dx[l])&&tx<=n&&(ty=k+dy[l])&&ty<=m&&(tt=h.ask(tx,ty))>0)add(cnt,tt);
				}else if(t>0&&Max(Abs(j-x[i]),Abs(k-y[i]))<=1)isok[t]=1;
		if(ncon()){
			puts("0");
			continue;
		}
		if(n==1||m==1){  //一行或一列可以特判
			puts("1");
			continue;
		}
		for(i=1;i<=cnt;++i){
			if(!pre[i])dfs(i,0);
			if(isok[i]&&iscut[i]){
				puts("1");
				ok=1;break;
			}
		}
		if(!ok)puts("2");
	}
	return 0;
}

写真経由:

見てくれてありがとう! 

おすすめ

転載: blog.csdn.net/asuf1364/article/details/132119859