如果把方向看做有向边,整个图是一个内向基环树。
所以考虑哪些点有可能放在基环树的非环部分上,当且仅当一个点周围有严格小于他的点。
由于图一定是二分图(黑白染色),没有奇环,所有偶环一定可以拆成二元环,所以可以看做找匹配。两个点能匹配当且仅当他们 \(s\) 相等。
发现一个周围没有严格小于他的点,必须要匹配。有的点可以参与匹配可以不参与匹配。所以可以跑有源汇上下界网络流。
// LUOGU_RID: 139219522
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+9,INF=1e9;
const char ch[]="UDLR";
int T,a[N],n,m,s,t,hd[N],e_num,hx[N],p,vh[N],q[N],l,r,b[N],v[N],g[N],in[N],ans;
struct edge{
int v,nxt,f;
}e[N<<4];
void add_edge(int u,int v,int f)
{
// printf("%d %d %d\n",u,v,f);
e[++e_num]=(edge){v,hd[u],f};
hd[u]=e_num;
e[++e_num]=(edge){u,hd[v],0};
hd[v]=e_num;
}
int bfs()
{
for(int i=0;i<=t;i++)
vh[i]=hd[i],v[i]=0;
v[q[l=r=1]=s]=1;
while(l<=r)
{
for(int i=hd[q[l]];i;i=e[i].nxt)
if(e[i].f&&!v[e[i].v])
v[q[++r]=e[i].v]=v[q[l]]+1;
++l;
}
return v[t];
}
int dfs(int x,int fl)
{
if(x==t)
return fl;
int k;
for(int&i=vh[x];i;i=e[i].nxt)
{
if(v[e[i].v]==v[x]+1&&e[i].f&&(k=dfs(e[i].v,min(fl,e[i].f))))
{
e[i].f-=k,e[i^1].f+=k;
return k;
}
}
return 0;
}
int dinic()
{
int ans=0,k;
while(bfs())
while(k=dfs(s,INF))
ans+=k;
return ans;
}
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m),p=n*m;
for(int i=0;i<p;i++)
scanf("%d",a+i);
for(int i=0;i<=p+3;i++)
in[i]=hd[i]=0,b[i]=-1;
ans=0;
e_num=1;
for(int i=0;i<p;i++)
{
int fl=0;
if((i%m^i/m)&1)
{
if(i-m>=0&&a[i-m]==a[i])
add_edge(i,i-m,1);
if(i+m<p&&a[i+m]==a[i])
add_edge(i,i+m,1);
if(i%m&&a[i-1]==a[i])
add_edge(i,i-1,1);
if(i%m^m-1&&a[i+1]==a[i])
add_edge(i,i+1,1);
}
if(i-m>=0&&a[i-m]<a[i])
fl=1;
if(i+m<p&&a[i+m]<a[i])
fl=1;
if(i%m&&a[i-1]<a[i])
fl=1;
if(i%m^m-1&&a[i+1]<a[i])
fl=1;
if(fl)
{
if((i%m^i/m)&1)
add_edge(p,i,1);
else
add_edge(i,p+1,1);
}
else
{
if((i%m^i/m)&1)
in[p]--,in[i]++;
else
in[p+1]++,in[i]--;
}
}
add_edge(p+1,p,INF);
for(int i=0;i<=p+1;i++)
{
if(in[i]<0)
add_edge(i,p+3,-in[i]);
else
add_edge(p+2,i,in[i]),ans+=in[i];
}
s=p+2,t=p+3;
int k=dinic();
if(k^ans)
{
puts("NO");
continue;
}
int s=1;
for(int i=0;i<p;i++)
{
int fl=0;
if((i%m^i/m)&1)
{
if(i-m>=0&&a[i-m]==a[i])
{
s+=2;
if(e[s].f)
b[i]=0,b[i-m]=1,g[i]=a[i]/2+(a[i]&1),g[i-m]=a[i]/2;
}
if(i+m<p&&a[i+m]==a[i])
{
s+=2;
if(e[s].f)
b[i]=1,b[i+m]=0,g[i]=a[i]/2+(a[i]&1),g[i+m]=a[i]/2;
}
if(i%m&&a[i-1]==a[i])
{
s+=2;
if(e[s].f)
b[i]=2,b[i-1]=3,g[i]=a[i]/2+(a[i]&1),g[i-1]=a[i]/2;
}
if(i%m^m-1&&a[i+1]==a[i])
{
s+=2;
if(e[s].f)
b[i]=3,b[i+1]=2,g[i]=a[i]/2+(a[i]&1),g[i+1]=a[i]/2;
}
}
if(i-m>=0&&a[i-m]<a[i])
fl=1;
if(i+m<p&&a[i+m]<a[i])
fl=1;
if(i%m&&a[i-1]<a[i])
fl=1;
if(i%m^m-1&&a[i+1]<a[i])
fl=1;
if(fl)
s+=2;
}
for(int i=0;i<p;i++)
{
if(b[i]==-1)
{
if(i-m>=0&&a[i-m]<a[i])
b[i]=0,g[i]=a[i]-a[i-m];
else if(i+m<p&&a[i+m]<a[i])
b[i]=1,g[i]=a[i]-a[i+m];
else if(i%m&&a[i-1]<a[i])
b[i]=2,g[i]=a[i]-a[i-1];
else if(i%m^m-1&&a[i+1]<a[i])
b[i]=3,g[i]=a[i]-a[i+1];
}
}
puts("YES");
for(int i=0;i<p;i++)
{
printf("%d ",g[i]);
if(i%m==m-1)
puts("");
}
for(int i=0;i<p;i++)
{
printf("%c ",ch[b[i]]);
if(i%m==m-1)
puts("");
}
}
}
标签:匹配,int,Showing,CF1416F,当且,基环树,&&,Off
From: https://www.cnblogs.com/mekoszc/p/17895504.html