题解:CF1284G Seollal
随 CF 时随到了这题,没思路,看了题解,因为我不会拟阵,所以只能研究 @Wu_Ren 大手子的题解,但并没有看懂。后来研究了好几天,自己想出来一个很好的理解方式,来提一下给大家看看。
主要的思想是,找到一组匹配后,每次添加一个未匹配的白点,从类似 Hall 定理的代数表达式上面,逐渐从不合法转为合法。下面是正文:
左上角的黑点太特殊了,这里我们先把它抠掉。和题解的前几步一样,一个容易发现的必要条件是,对任意非空黑点集合
接下来我们要证明,这个条件也是充分的。不过只有证明还不够,因此接下来会有一个构造性的证明。
因为有
这说明,存在一个未匹配的白点。任取这样一个点
那么对于
而
最后你就去看,是否还存在
- 对所有黑点
y ,连接y 与p_y 。 - 对所有黑点
y ,找到第一次使得v_y=1 的点x ,连接y 与x 。
这样每个黑点刚好连了两条边,容易通过极端原理说明无环。因为边数还不够连通,所以随便再添加一些边使得形成一棵树就可以了,用并查集实现很容易。复杂度瓶颈在于匹配,Dinic 可以做到
#include<bits/stdc++.h>
using namespace std;
struct MF{
int n,S,T;
int hd[100005],nxt[300005],c[300005],dy[300005],tot=1;
int cur[100005],l[100005];
void addedge(int u,int v,int w){
nxt[++tot]=hd[u],hd[u]=tot;
c[tot]=w,dy[tot]=v;
}
void adde(int u,int v,int w){
addedge(u,v,w);addedge(v,u,0);
}
long long ans;
void clear(){
for(int i=1;i<=n;++i)hd[i]=0;
for(int i=1;i<=tot;++i)nxt[i]=c[i]=dy[i]=0;
tot=1;ans=0;
}
bool getc(){
for(int i=1;i<=n;++i)l[i]=1e9,cur[i]=hd[i];
l[S]=0;
queue<int>q;
q.emplace(S);
while(q.size()){
int x=q.front();
q.pop();
for(int i=hd[x];i;i=nxt[i]){
if(!c[i])continue;
int cu=dy[i];
if(l[cu]>l[x]+1){
l[cu]=l[x]+1;
q.emplace(cu);
}
}
}
return l[T]<1e9;
}
int dinic(int x,int rl){
if(x==T){
ans+=rl;
return rl;
}
int rr=rl;
for(int i=cur[x];i;i=nxt[i]){
cur[x]=i;
if(!c[i])continue;
int cu=dy[i];
if(l[cu]!=l[x]+1)continue;
int zz=dinic(cu,min(rr,c[i]));
c[i]-=zz,c[i^1]+=zz;
rr-=zz;
if(!rr)break;
}
return rl-rr;
}
int liu(){
while(getc())dinic(S,INT_MAX);
return ans;
}
}M;
char s[25][25],ans[45][45];
#define mk(x,y) (((x)-1)*m+(y))
int mb[4][2]={{0,1},{0,-1},{1,0},{-1,0}};
int n,m,pp[405],ok[405],fa[405];
vector<int>gg[405];
int findfather(int x){
return x==fa[x]?x:fa[x]=findfather(fa[x]);
}
void jia(int x,int y){
int fx=findfather(x),fy=findfather(y);
if(fx!=fy){
int a1=(x-1)/m+1,a2=(x-1)%m+1,b1=(y-1)/m+1,b2=(y-1)%m+1;
ans[a1+b1-1][a2+b2-1]='O';fa[fx]=fy;
}
}
void dfs(int x){
for(auto cu:gg[x]){
if(ok[cu]||pp[cu]==x)continue;
jia(cu,x),ok[cu]=1,dfs(pp[cu]);
}
}
int main(){
int T;scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
for(int i=1;i<=2*n-1;++i){
for(int j=1;j<=2*m-1;++j)ans[i][j]=' ';
ans[i][2*m]=0;
}
M.n=n*m+2,M.S=n*m+1,M.T=n*m+2;
M.clear();
int c1=0;
for(int i=1;i<=n*m;++i)pp[i]=ok[i]=0,fa[i]=i,gg[i].clear();
for(int i=1;i<=n;++i)scanf("%s",s[i]+1);
for(int i=1;i<=n;++i)for(int j=1;j<=m;++j){
ans[2*i-1][2*j-1]=s[i][j];
if(i+j>2&&s[i][j]=='O'){
if((i+j)%2==0){
M.adde(M.S,mk(i,j),1),++c1;
for(int k=0;k<4;++k){
int dx=i+mb[k][0],dy=j+mb[k][1];
if(dx<1||dx>n||dy<1||dy>m)continue;
if(s[dx][dy]=='O'){
M.adde(mk(i,j),mk(dx,dy),1);
gg[mk(dx,dy)].emplace_back(mk(i,j));
}
}
}else M.adde(mk(i,j),M.T,1);
}
}
if(M.liu()!=c1){
puts("NO");continue;
}
for(int i=M.hd[M.S];i;i=M.nxt[i]){
int cu=M.dy[i];
for(int j=M.hd[cu];j;j=M.nxt[j])if(!M.c[j]){
pp[cu]=M.dy[j];pp[pp[cu]]=cu;jia(cu,pp[cu]);
}
}
for(int i=1;i<=n;++i)for(int j=1;j<=m;++j){
if((i+j)%2==0||s[i][j]!='O'||pp[mk(i,j)])continue;
dfs(mk(i,j));
}
int fl=1;
for(int i=1;i<=n;++i)for(int j=1;j<=m;++j){
if((i+j)%2||s[i][j]!='O'||i+j==2)continue;
if(!ok[mk(i,j)])fl=0;
}
if(!fl){
puts("NO");continue;
}
for(int i=1;i<=n;++i)for(int j=1;j<=m;++j){
if(s[i][j]!='O')continue;
if(i<n&&s[i+1][j]=='O')jia(mk(i,j),mk(i+1,j));
if(j<m&&s[i][j+1]=='O')jia(mk(i,j),mk(i,j+1));
}
puts("YES");
for(int i=1;i<=2*n-1;++i)printf("%s\n",ans[i]+1);
}
return 0;
}