非常有意思的一道题(兔子才没有那么蠢)
首先最小割模型很好看出来。但是点数1e6边数1e6的话不加些7788的优化大概是过不去的(不可能过去的这辈子不可能过去的就算你卡常的奇技淫巧高超良心出题人肯定还是要有数据卡你的)
然后我们就需要探索这个题的深层性质
我们发现它是一个边数很少的图,并且还是平面图。
平面图最小割=对偶图最短路!!!
好了我们来复习一下平面图的相关性质(摘自:周冬《浅析最大最小定理在信息学竞赛中的应用》)
定义:平面图是可以画在平面上并且使得不同的边可以互不交叠的图。(有点蠢但是我最开始真的不知道QAQ)
欧拉公式:平面图中点数为n边数为m面数为f则有f=m-n+2
对偶图:对于每一个平面图都有一个与之对应的对偶图,对偶图中的点代表平面图中的面。
建图:对于一条边E有两种情况:
(A) E的两侧分别是两个面 加入边(*f1,*f2)
(B) E被一个平面完全包含 加入边(*f,*f)[自环]
一些性质(G表示原图 *G表示对偶图)
G的面数=*G的点数 G的边数=*G的边数
*G中的环与G的割对应
还有一个很重要的前提:源点和汇点需要在无界面的边界上,这样的平面图叫做s-t平面图。
整体的建图大概是这么一个过程
1.连接s-t(当然要跨过所有点啦emm不然就不是平面图了啊喂)将无界面分成两个部分圈起来的那块看做源点即可
2.按照上面的定义连边就吼啦
我们来把样例画一画
有一点点扭曲(雾)
于是我们回到了开始的那个非常nb的结论:平面图的最小割=对偶图的最短路!
所以这个题的时间复杂度从网络流的O(玄学)降到了O(nlgn)
哦对了这个题巨坑。。。n=1或m=1需要判掉。。。WA了一发懵逼了看了一眼题解然后卧槽还有这个样子的诶。。。
附代码。
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<queue>
#define mxn 2100
using namespace std;
struct edge{int to,lt,v;}e[mxn*mxn<<1];
struct node
{
int x,dis;
node(){}
node(int _x,int _dis){x=_x,dis=_dis;}
};
bool operator <(node a,node b){return a.dis>b.dis;}
priority_queue<node> que;
int in[mxn*mxn],cnt,s,t,dis[mxn*mxn],n,m;bool vis[mxn*mxn];
void add(int x,int y,int v){e[++cnt].lt=in[x];e[cnt].to=y;e[cnt].v=v;in[x]=cnt;}
void addedge(int x,int y,int v){add(x,y,v);add(y,x,v);}
void dij()
{
memset(dis,48,sizeof(dis));
dis[s]=0;que.push(node(s,0));
while(!que.empty())
{
int x=que.top().x;que.pop();
if(vis[x]) continue;vis[x]=1;
for(int i=in[x];i;i=e[i].lt)
{
int y=e[i].to;
if(dis[y]>dis[x]+e[i].v)
dis[y]=dis[x]+e[i].v,que.push(node(y,dis[y]));
}
}
}int mm;
int id(int x,int y,int f)
{
//printf("%d %d %d=%d\n",x,y,f,(((x-1)*mm+(y-1))<<1)+f+1);
return (((x-1)*mm+(y-1))<<1)+f+1;
}
int main()
{
int i,j,val;
scanf("%d%d",&n,&m);
if(n==1 || m==1)
{
int ans=999999999,t;
if(m<n) swap(n,m);
for(i=1;i<=m;i++) scanf("%d",&t),ans=min(ans,t);
printf("%d\n",ans);
return 0;
}
s=((n-1)*(m-1))<<1|1;t=s+1;mm=m-1;
for(i=1;i<=n;i++)
{
for(j=1;j<m;j++)
{
scanf("%d",&val);
if(i==1) addedge(s,id(i,j,1),val);
else if(i==n) addedge(id(i-1,j,0),t,val);
else addedge(id(i-1,j,0),id(i,j,1),val);
}
}
for(i=1;i<n;i++)
{
for(j=1;j<=m;j++)
{
scanf("%d",&val);
if(j==1) addedge(t,id(i,j,0),val);
else if(j==m) addedge(id(i,j-1,1),s,val);
else addedge(id(i,j-1,1),id(i,j,0),val);
}
}
for(i=1;i<n;i++)
for(j=1;j<m;j++)
{
scanf("%d",&val);
addedge(id(i,j,0),id(i,j,1),val);
}
dij();
printf("%d\n",dis[t]);
return 0;
}