CF1416F Showing Off

Statement

一开始有一张 n×m 的网格图,一开始,每个节点 (i,j)(1in,1jm) 会连恰好一条有向 边,指向 (i,j+1),(i,j1),(i+1,j),(i1,j) 其中一个(当然,这个指向的点要在网格上,不能到边界外)。一开始每个点带有一个点权 wi,j1,记 Si,j(i,j) 在图上能到达的点的 w 之和。 现在你只知道 n,m,{Si,j},请求出一种合法的 wi,j 和连边方案,使得 {Si,j} 正确。

1nm105,Si,j[2,109]N

Solution

显然,我们最后连成的图会是一个基环内向树森林

由于这是一个网格图,黑白染色容易发现我们的基环必然是一个偶环,且基环上面所有点的 S 相同

我们可以贪心地把基环拆成若干个二元环,这样更容易满足 S 相同的要求

显然,基环树上,父亲的 S 小于儿子

现在等价于这样的问题:每个点可以直接成为自己旁边某个 S 小于自己的点的儿子,或者和自己旁边某个和自己 S 相同的点组成二元环,且必须选其中之一。

一个点显然不能连向大于它的点。

假如一个点旁边的点都 它,那么它必须选择一个权值相同的点

一个点 (i,j) 如果旁边有小于自己的点 (x,y),那么它可以不选择权值相同的点。

注意到假若一个点周围所有的点的值都大于它,那么它无法连边,即无解。

即要求做一个二分图匹配(黑白染色,黑点为左部点,白点为右部点),但某些点必须在匹配里

这里的处理方式是跑一个有源汇上下界可行流,对于一个必须匹配的黑点 u ,那么从源点向 u 连边 [1,1]u,v 连边 [0,1]v 连边汇点 [1,1]

可惜这样的问题在于,它最后形成的图不是二分图,大概长这样:

所以它不是二分图,所以复杂度是没有保证的,但是本题比较良心,没有卡,可以冲

\textcolorbalckp\textcolorred\_b\_p\_b 给出了这样一个 trick

做一个新图 G。考虑拎出必须匹配的左部点和所有右部点,跑二分图匹配,如果没有完备匹配说明无解,否则视为在 G 上,每个左部点向它匹配的右部点连一条边。

然后拎出所有左部点和必须匹配的右边点,类似地做,在 G 上,每个右部点向它匹配的左部点连一条边。

此时发现在 G 上,每个点的度数至多为 2,所以只有环和链。

对于环,发现全都是偶环(二分图),隔一条边取一条边作为匹配边,显然环上的每个点都在匹配里了。

对于链,从链开头那条边开始,隔一条边取一条边作为匹配边(比如 ,1234 就取 (1,2),(3,4)

这样如果是偶链的话是可以的,可是奇环的话链最后一个点不在匹配内

仔细思考发现最后一个点其实并不是必须选择的点(不然它就有出边),所以不用管。

这样跑两次二分图匹配就可以得到一个可行的匹配了,复杂度 O(nmnm)

Code

这里给出网络流爆冲的代码

#include<bits/stdc++.h>
#define id(x,y) ((x-1)*m+y)
using namespace std;
const int N = 2e5+5;

char buf[1<<23],*p1=buf,*p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
int read(){
    int s=0,w=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
    while(isdigit(ch))s=s*10+ch-'0',ch=getchar();
    return s*w;
}

struct Edge{int nex,to,dis;}edge[N<<2];
int head[N],cur[N],dep[N],S[N],w[N],U[N<<3],V[N<<3];
char op[]={'L','R','U','D'},Ud[N<<2],Vd[N<<2],C[N];
int dx[]={0,0,-1,1},dy[]={-1,1,0,0};
int T,n,m,s,t,ss,tt,cnt,elen,give,rec;
queue<int>q;

void addedge(int u,int v,int w){
    if(!w)return ;//
    edge[++elen]=(Edge){head[u],v,w},head[u]=elen;
    edge[++elen]=(Edge){head[v],u,0},head[v]=elen;
    U[elen]=ss;
}
void addedge(int u,int v,int L,int R){
    addedge(ss,v,L),addedge(u,v,R-L),addedge(u,tt,L),give+=L;//在差网络上操作
}
bool bfs(){
    memset(dep,0,(cnt+1)*4);
    dep[ss]=1,q.push(ss);
    while(q.size()){
        int u=q.front(); q.pop();
        for(int e=head[u],v;v=edge[e].to,e;e=edge[e].nex)
            if(!dep[v]&&edge[e].dis)dep[v]=dep[u]+1,q.push(v);
    }
    return dep[tt]!=0;
}
int dfs(int u,int flow){
    if(u==tt)return flow; int res=0;
    for(int&e=cur[u],v;v=edge[e].to,e;e=edge[e].nex)
        if(dep[v]==dep[u]+1&&edge[e].dis){
            int tmp=dfs(v,min(flow,edge[e].dis));
            edge[e].dis-=tmp,flow-=tmp;
            edge[e^1].dis+=tmp,res+=tmp;
            if(!flow)return res;
        }
    return res;
}
void Dinic(){
    while(bfs())
        memcpy(cur,head,(cnt+1)*4),
        rec+=dfs(ss,1e9);
}

signed main(){
    T=read();
    while(T--){
        n=read(),m=read(),elen=1,give=rec=0;
        cnt=id(n,m),s=++cnt,t=++cnt,ss=++cnt,tt=++cnt;
        for(int i=1;i<=cnt;++i)head[i]=0;
        for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)S[id(i,j)]=read();
        
        for(int i=1;i<=n;++i)for(int j=1,l;l=1,j<=m;++j){
            for(int k=0;k<=3;++k){
                int x=i+dx[k],y=j+dy[k];
                if(x<1||x>n||y<1||y>m)continue;
                if(S[id(x,y)]<S[id(i,j)])
                    l=0,C[id(i,j)]=op[k],w[id(i,j)]=S[id(i,j)]-S[id(x,y)];
                //这里的实现是直接先取了而不连边,防止找不到匹配
                if(S[id(x,y)]==S[id(i,j)]&&((i^j)&1))
                    addedge(id(i,j),id(x,y),1),
                    U[elen]=id(i,j),V[elen]=id(x,y),
                    Ud[elen]=op[k],Vd[elen]=op[k^1];
                //这里 U[elen]=id(i,j) 和 addedge 中的 U[elen]=ss 联动,来区分掉 (s,id(i,j))(下面的连边)
            }
            if((i^j)&1)addedge(s,id(i,j),l,1);//下界 l 
            else addedge(id(i,j),t,l,1);
        }

        addedge(t,s,1e9),Dinic();
        if(give!=rec){puts("NO");continue;}//流出流入不相同,说明存在一个点所有旁边的点的 S 都比它大
        for(int i=3;i<=elen;i+=2)if(U[i]!=ss&&edge[i].dis)//这里遍历的是反向边
            C[U[i]]=Ud[i],C[V[i]]=Vd[i],w[U[i]]=1,w[V[i]]=S[U[i]]-1;

        puts("YES");
        for(int i=1;i<=n;++i,puts(""))for(int j=1;j<=m;++j)printf("%d ",w[id(i,j)]);
        for(int i=1;i<=n;++i,puts(""))for(int j=1;j<=m;++j)printf("%c ",C[id(i,j)]);
    }
    return 0;
}
posted @   _Famiglistimo  阅读(79)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
历史上的今天:
2021-02-11 「JSOI2007」文本生成器 题解
2021-02-11 [POI2006]OKR-Periods of Words 题解
点击右上角即可分享
微信分享提示