说在前面的话
让我们一起来赞美凉心的出题人吧!
我被空间限制折腾的死去活来,建议出题人直接爪巴。
题解
首先我们需要建图(废话),一种比较巧妙的建图方式是将每一个方格拆点,拆为横向和纵向两个方向的点,然后横向的点跟左右横向的点连,纵向的点和上下纵向的点连,改变方向只有在一个方向上碰壁了才可以,就是自己横向连向自己的纵向或纵向连横向。需要注意的是,上述操作连的所有边都是有向的。
然后你就得到了一张有向图,你可以跑一个缩点然后把它变成一张 ( ext{DAG}) ,我们现在需要求的就是能否可以从起点出发,走一条路径并且经过所有的星星。
直接做好像不是很行,但是我们可以发现一个节点只有走与不走两种情况,同时一个星星最多同时属于两个强连通分量,我们可以用 ( ext{2-sat}) 来维护,即星星的两个强连通分量至少选一个,在 ( ext{DAG}) 上没有前后继关系的节点至多选择一个,然后跑一下 ( ext{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
",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("------------
");
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
",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("------------
");
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("------------
");
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
");return 0;
}
}
}
// printf("------------
");
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
",bel[id[Id(i,j)][1]],bel[id[Id(i,j)][0]]);
}
}//2-sat
// printf("------------
");
for(int i=1;i<=cnt_bel*2;++i) if(!shit.dfn[i]) shit.tarjan(i);
// printf("------------
");
for(int i=1;i<=cnt_bel;++i){
if(shit.bel[i]==shit.bel[i+cnt_bel]){
printf("NO
");return 0;
}
}
printf("YES
");
return 0;
}