传送门:bzoj1001
题解
先贴一篇博客对偶图的应用
平面图的两个性质:
- 无重边和自环的无向图
- 所有边只在端点相交
关于平面图的度和相邻面的介绍上面贴的博客里都有。
平面图都可以转对偶图。
建立方法就是将平面图的每个面(包括外部面)看做一个节点。根据平面图上的每条边,将其划分的两个面所代表的顶点在对偶图中连接一条无向边,若边带权,则边权与原边相同。
接下来说这道题的做法。
首先想到最小割,转最大流。
但是
n2m
n
2
m
的复杂度不科学。
考虑平面图转化为对偶图的用处,根据这张从网上扒的图:
可知因为连接了外界的点,整个图存在一个经过
s′−>t′
s
′
−
>
t
′
的环。实际上最小的环就是原图的最小割。因为
s′−>t′
s
′
−
>
t
′
这条边造成了环,所以删掉这条边,跑一遍
s′−>t′
s
′
−
>
t
′
的最短路就是原图的最小割。
代码
堆优化dijkstra
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+100;
const int inf=2e9;
int S,T;
int head[N<<1],to[N*6],nxt[N*6],w[N*6],tot;
int n,m;
inline void lk(int u,int v,int cc)
{
to[++tot]=v;nxt[tot]=head[u];head[u]=tot;w[tot]=cc;
to[++tot]=u;nxt[tot]=head[v];head[v]=tot;w[tot]=cc;
}
char c;
inline int rd()
{
c=getchar();int x=0,f=1;
for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
for(;isdigit(c);c=getchar()) x=x*10+(c^48);
return x*f;
}
struct dij{
int id[N<<1],tg[N<<1],dis[N<<1],cnt;
dij(){cnt=0;}
inline void upp(int x)
{
x=tg[x];int k=x>>1;
for(;dis[id[k]]>dis[id[x]] && k;x=k,k>>=1){
swap(tg[id[k]],tg[id[x]]);
swap(id[k],id[x]);
}
}
inline void dnn(int x)
{
x=tg[x];int k=x<<1;
if(k<cnt && dis[id[k]]>dis[id[k|1]]) k|=1;
for(;k<=cnt && dis[id[k]]<dis[id[x]];){
swap(tg[id[k]],tg[id[x]]);
swap(id[x],id[k]);
x=k;
k<<=1;if(k<cnt && dis[id[k]]>dis[id[k|1]]) k|=1;
}
}
inline void ins(int x)
{
cnt++;id[cnt]=x;tg[x]=cnt;
if(cnt==1) return;
upp(x);
}
inline int getop(){return id[1];}
}G;
int main(){
int i,j,bs,x,res;
scanf("%d%d",&n,&m);
if(n==m && n==1) {puts("0");return 0;}
if(n==1 || m==1){
if(n==1) swap(n,m);//m==1 -> n==1 这里打错才真的调得久
res=inf;
for(i=1;i<n;++i) res=min(res,rd());
printf("%d\n",res);
return 0;
}
S=(n-1)*(m-1)*2+1;T=S+1;
for(j=1;j<m;++j) lk(S,j,rd());
for(bs=0,i=2;i<n;++i){
bs+=(m-1)+(m-1);
for(j=1;j<m;++j) lk(bs+j-m+1,bs+j,rd());
}
for(j=1;j<m;++j)
lk(bs+m-1+j,T,rd());
for(bs=0,i=1;i<n;++i,bs+=(m-1)+(m-1)){
lk(T,bs+m,rd());
for(j=1;j<m-1;++j) lk(bs+j,bs+m+j,rd());
lk(bs+m-1,S,rd());
}
for(bs=0,i=1;i<n;++i,bs+=(m-1)+(m-1))
for(j=1;j<m;++j)
lk(bs+j,bs+m-1+j,rd());
for(i=1;i<=T;++i) G.dis[i]=inf,G.ins(i);
G.dis[S]=0;G.upp(S);
for(;;){
x=G.getop();res=G.dis[x];
if(x==T) break;
G.dis[x]=inf+1;G.dnn(x);
for(i=head[x];i;i=nxt[i]){
j=to[i];if(G.dis[j]<=inf && G.dis[j]>res+w[i]){
G.dis[j]=res+w[i];
G.upp(j);
}
}
}
printf("%d\n",G.dis[T]);
}