GYM102059B Dev, Please Add This!

说在前面的话

让我们一起来赞美凉心的出题人吧!

我被空间限制折腾的死去活来,建议出题人直接爪巴。

题解

首先我们需要建图(废话),一种比较巧妙的建图方式是将每一个方格拆点,拆为横向和纵向两个方向的点,然后横向的点跟左右横向的点连,纵向的点和上下纵向的点连,改变方向只有在一个方向上碰壁了才可以,就是自己横向连向自己的纵向或纵向连横向。需要注意的是,上述操作连的所有边都是有向的。

然后你就得到了一张有向图,你可以跑一个缩点然后把它变成一张 \(\text{DAG}\) ,我们现在需要求的就是能否可以从起点出发,走一条路径并且经过所有的星星。

直接做好像不是很行,但是我们可以发现一个节点只有走与不走两种情况,同时一个星星最多同时属于两个强连通分量,我们可以用 \(\text{2-sat}\) 来维护,即星星的两个强连通分量至少选一个,在 \(\text{DAG}\) 上没有前后继关系的节点至多选择一个,然后跑一下 \(\text{tarjan}\) 就可以了。

代码

#pragma GCC optimize("Ofast")

#include<bits/stdc++.h>
using namespace std;
const int N=105;
int n,m,x,y;
int mp[N*N],id[N*N][2],tot=0;
int Id(int x,int y){return x*(m+2)+y;}
struct Edge{int nxt,from,to;}e[N*N];int fir[N*N],cnt_Edge=0;
void add(int u,int v){e[++cnt_Edge]=(Edge){fir[u],u,v},fir[u]=cnt_Edge;}
int dfn[N*N],low[N*N],cnt_dfn=0;
stack<int> s;bool tag[N*N];
int bel[N*N],cnt_bel=0;
void tarjan(int u){
	// printf("%d\n",u);
	dfn[u]=low[u]=++cnt_dfn;
	s.push(u),tag[u]=true;
	for(int i=fir[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(!dfn[v]) tarjan(v),low[u]=min(low[u],low[v]);
		else if(tag[v]) low[u]=min(low[u],dfn[v]);
	}
	if(dfn[u]==low[u]){
		++cnt_bel;
		while(s.top()!=u)
		bel[s.top()]=cnt_bel,tag[s.top()]=false,s.pop();
		bel[s.top()]=cnt_bel,tag[s.top()]=false,s.pop();
	}
}
Edge E[2][N*N];int Fir[2][N*N],Cnt_Edge=0;
void Add(int u,int v,int tag){
	E[tag][++Cnt_Edge]=(Edge){Fir[tag][u],u,v},Fir[tag][u]=Cnt_Edge;
}
void add_tag(int x){
	queue<int> q;
	q.push(x),tag[x]=true;
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=Fir[0][u];i;i=E[0][i].nxt){
			int v=E[0][i].to;
			if(!tag[v]) q.push(v),tag[v]=true;
		}
		
	}
	q.push(x);
	while(!q.empty()){
		int u=q.front();q.pop();
		for(int i=Fir[1][u];i;i=E[1][i].nxt){
			int v=E[1][i].to;
			if(!tag[v]) q.push(v),tag[v]=true;
		}
	}
}
#define MEMORY 21474836
struct Two_Sat{
	Edge e[MEMORY];int fir[N*N],size=0;
	void add(int u,int v){e[++size]=(Edge){fir[u],u,v},fir[u]=size;}
	int dfn[N*N],low[N*N],cnt_dfn=0;
	stack<int> s;bool tag[N*N];
	int bel[N*N],cnt_bel=0;
	void tarjan(int u){
		dfn[u]=low[u]=++cnt_dfn;
		s.push(u),tag[u]=true;
		for(int i=fir[u];i;i=e[i].nxt){
			int v=e[i].to;
			if(!dfn[v]) tarjan(v),low[u]=min(low[u],low[v]);
			else if(tag[v]) low[u]=min(low[u],dfn[v]);
		}
		if(dfn[u]==low[u]){
			++cnt_bel;
			while(s.top()!=u)
			bel[s.top()]=cnt_bel,tag[s.top()]=false,s.pop();
			bel[s.top()]=cnt_bel,tag[s.top()]=false,s.pop();
		}
	}
}shit;//这一定要建这么多的图吗?我爪巴了。
int main(){
	// freopen("data.in","r",stdin);
	// freopen("jd.out","w",stdout);
	cin>>n>>m;
	for(int i=1;i<=n;++i) mp[Id(i,0)]=mp[Id(i,m+1)]=-1;
	for(int i=1;i<=m;++i) mp[Id(0,i)]=mp[Id(n+1,i)]=-1;
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			char c;do c=getchar();
			while(c!='#'&&c!='.'&&c!='*'&&c!='O');
			if(c=='.') mp[Id(i,j)]=0;
			if(c=='#') mp[Id(i,j)]=-1;
			if(c=='*') mp[Id(i,j)]=1;
			if(c=='O') mp[Id(i,j)]=0,x=i,y=j;
			if(mp[Id(i,j)]>=0){
				id[Id(i,j)][0]=++tot;//heng
				id[Id(i,j)][1]=++tot;//shu
			}
		}
	}
	// printf("------------\n");
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			if(mp[Id(i,j)]<0) continue;
			// printf("%d %d %d %d\n",i,j,id[Id(i,j)][0],id[Id(i,j)][1]);
			if(mp[Id(i,j-1)]>=0) add(id[Id(i,j)][0],id[Id(i,j-1)][0]);
			if(mp[Id(i,j+1)]>=0) add(id[Id(i,j)][0],id[Id(i,j+1)][0]);
			if(mp[Id(i-1,j)]>=0) add(id[Id(i,j)][1],id[Id(i-1,j)][1]);
			if(mp[Id(i+1,j)]>=0) add(id[Id(i,j)][1],id[Id(i+1,j)][1]);
			if(mp[Id(i,j-1)]<0||mp[Id(i,j+1)]<0) add(id[Id(i,j)][0],id[Id(i,j)][1]);
			if(mp[Id(i-1,j)]<0||mp[Id(i+1,j)]<0) add(id[Id(i,j)][1],id[Id(i,j)][0]);
		}
	}//有向图
	// printf("------------\n");
	if(!dfn[id[Id(x,y)][0]]) tarjan(id[Id(x,y)][0]);
	if(!dfn[id[Id(x,y)][1]]) tarjan(id[Id(x,y)][1]);
	// printf("------------\n");
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			if(mp[Id(i,j)]<=0) continue;
			if(!bel[id[Id(i,j)][0]]&&!bel[id[Id(i,j)][1]]){
				printf("NO\n");return 0;
			}
		}
	}
	// printf("------------\n");
	for(int i=1;i<=cnt_Edge;++i){
		if(!bel[e[i].from]||!bel[e[i].to]) continue;
		if(bel[e[i].from]!=bel[e[i].to]){
			Add(bel[e[i].from],bel[e[i].to],0);
			Add(bel[e[i].to],bel[e[i].from],1);
		}
	}//DAG
	for(int i=1;i<=cnt_bel;++i){
		for(int j=1;j<=cnt_bel;++j) tag[j]=false;
		add_tag(i);
		for(int j=1;j<=cnt_bel;++j)
		if(!tag[j]) shit.add(i+cnt_bel,j);
	}
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			if(mp[Id(i,j)]<=0) continue;
			if(!bel[id[Id(i,j)][0]]){
				shit.add(bel[id[Id(i,j)][1]],bel[id[Id(i,j)][1]]+cnt_bel);
				continue;
			}
			if(!bel[id[Id(i,j)][1]]){
				shit.add(bel[id[Id(i,j)][0]],bel[id[Id(i,j)][0]]+cnt_bel);
				continue;
			}
			if(bel[id[Id(i,j)][0]]==bel[id[Id(i,j)][1]]){
				shit.add(bel[id[Id(i,j)][0]],bel[id[Id(i,j)][0]]+cnt_bel);
				continue;
			}
			shit.add(bel[id[Id(i,j)][0]],bel[id[Id(i,j)][1]]+cnt_bel);
			shit.add(bel[id[Id(i,j)][1]],bel[id[Id(i,j)][0]]+cnt_bel);
			// printf("%d %d\n",bel[id[Id(i,j)][1]],bel[id[Id(i,j)][0]]);
		}
	}//2-sat
	// printf("------------\n");
	for(int i=1;i<=cnt_bel*2;++i) if(!shit.dfn[i]) shit.tarjan(i);
	// printf("------------\n");
	for(int i=1;i<=cnt_bel;++i){
		if(shit.bel[i]==shit.bel[i+cnt_bel]){
			printf("NO\n");return 0;
		}
	}
	printf("YES\n");
	return 0;
}
posted @ 2021-02-21 19:24  Point_King  阅读(211)  评论(0编辑  收藏  举报