「CEOI2019」建造摩天楼

「CEOI2019」建造摩天楼

显然是倒着考虑删除每个大楼,此时每次面临的情况都是一个子问题

下文称当前局面未被删除的大楼为黑点,其余为白点

子问题有解的充要条件是:黑点之间能 8-连通

当前一个点能够被删掉的条件是:

1.这个点能够连通到无穷处

2.这个点不是当前8-连通图的割点

[ ]

考虑用一个简单的方法维护条件1:

将一开始每个黑点周围的白点取出,按照白点之间4-连通构建连通块

能够4-连通接触到最外层连通块的黑点满足条件1

每次删除黑点之后,增加能够连通的白点,每个白点只会增加一次

ps:寻找最外层4-连通白点的一个方法:找到x最大的白点

[ ]

接下来考虑如何判定一个点(u)是否是割点:

首先删除(u)之后,周围8连通内能够构成多个连通块,可以发现大致可以归结为以下几种情况,其中x,y为黑点

多个(x)表示(x)在其中任何一个位置都可以

1.
x y
x u y
x y
2.
x y
u y
y y y

对于这两种情况,只要白点1和白点2在同一4-连通块,割掉(u)就会分开x和(y)

由此,每次插入一个白点,可以(O(1))检测一个点是否合法

简单讨论可以发现,会被影响合法性的点,一定在新加入最外层连通块的白点周围

这样总共check了(O(n))次,每次用一个堆/set维护能选的最大编号的点即可

ps:代码非常丑非常垃圾。。。

#include<bits/stdc++.h>
using namespace std;

typedef pair <int,int> Pii;
#define mp make_pair
#define pb push_back
#define x first
#define y second
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)

char IO;
template <class T=int> T rd(){
	T s=0; int f=0;
	while(!isdigit(IO=getchar())) f|=IO=='-';
	do s=(s<<1)+(s<<3)+(IO^'0');
	while(isdigit(IO=getchar()));
	return f?-s:s;
}

const int N=1.3e6+10,INF=1e9+10;
const int z[5][4]={{1,0},{0,-1},{-1,0},{0,1}};
const int Z[9][4]={{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1,0},{-1,1},{0,1}};

int n,m,k;
int F[N];
int Find(int x){ return F[x]==x?x:F[x]=Find(F[x]);}
void Union(int x,int y){ F[Find(x)]=Find(y); }
map <Pii,int> B,W;
set <int> st;
vector <int> ans;
Pii A[N],P[N];
int ma=-1e9,maxid;
int G[N][4];
void Ins(int x,int y){
    // Insert white point
	Pii T=mp(x,y);
	if(B.count(T)) return ;
	if(!W.count(T)) P[W[T]=++m]=T,F[m]=m;
	int u=W[T];
	if(x>ma) ma=x,maxid=u;
	rep(i,0,3) {
		int x1=x+z[i][0],y1=y+z[i][1];
		if(W.count(mp(x1,y1))) {
			int v=W[mp(x1,y1)];
			G[u][i]=v;
			G[v][(i+2)%4]=u;
			Union(u,v);
		}
	}
}

int del[N],vis[N],reach[N];
int Check(int u){
	if(del[u]||!reach[u]) return 0;
	static int I[8];
	int c=0;
	rep(i,0,7) {
		Pii T=mp(A[u].x+Z[i][0],A[u].y+Z[i][1]);
		if(W.count(T)) I[i]=Find(W[T]);
		else I[i]=0,c++;
	}
	for(int i=1,t=0;t<4;t++,i=(i+2)%8){
		int j=(i+2)%8;
		if(I[i] && I[i]==I[j] && !I[(i+1)%8] && c>1) return 0;
	}
	if((!I[0]||!I[1]||!I[2]) && (!I[4]||!I[5]||!I[6]) && I[3] && I[3]==I[7]) return 0;
	if((!I[2]||!I[3]||!I[4]) && (!I[6]||!I[7]||!I[0]) && I[1] && I[5]==I[1]) return 0;
	return 1;
}
void ReCheck(int u){
	auto it=st.find(u);
	if(it!=st.end()) st.erase(it);
	if(Check(u)) st.insert(u);
}

vector <int> tmp;
void dfs(int u){
	if(!u) return;
	if(vis[u]) return;
	vis[u]=1;
	rep(i,0,3) {
		if(G[u][i]) dfs(G[u][i]);
		Pii T=mp(P[u].x+z[i][0],P[u].y+z[i][1]);
		if(B.count(T)) reach[B[T]]=1;
	}
	rep(dx,-1,1) rep(dy,-1,1) if(dx||dy) {
		Pii T=mp(P[u].x+dx,P[u].y+dy);
		if(B.count(T)) tmp.pb(B[T]);
	}
}
void Dfs(int u){
	dfs(u);
	sort(tmp.begin(),tmp.end()),tmp.erase(unique(tmp.begin(),tmp.end()),tmp.end());
	for(int i:tmp) ReCheck(i);
	tmp.clear();
}

void Del(int u){
    // delete black point
	del[u]=1;
	B.erase(A[u]),Ins(A[u].x,A[u].y);
	Dfs(W[A[u]]);
}

int main(){
	n=rd(),rd();
	rep(i,1,n) A[i].x=rd(),A[i].y=rd(),B[A[i]]=i;
	rep(i,1,n) F[i]=i;
	
	// Check NO
	rep(i,1,n) { 
		rep(dx,-1,1) rep(dy,-1,1) {
			Pii T=mp(A[i].x+dx,A[i].y+dy);
			if(B.count(T)) Union(i,B[T]);
		}
	}
	rep(i,1,n) if(Find(i)!=Find(1)) return puts("NO"),0;

	rep(i,1,n) rep(dx,-1,1) rep(dy,-1,1) if(dx||dy) Ins(A[i].x+dx,A[i].y+dy);
	Dfs(maxid);

	rep(i,1,n) {
		int u=*st.rbegin();
		auto it=st.end(); --it;st.erase(it);
		Del(u),ans.pb(u);
	}
	reverse(ans.begin(),ans.end());
	puts("YES");
	for(int i:ans) printf("%d
",i);
}
原文地址:https://www.cnblogs.com/chasedeath/p/14446391.html