海拔
对偶图的学习笔记。
首先这道题要求的是一个最小割,但数据范围太大写最小割会爆炸。于是按照题解中的天才思路考虑把平面内某个由点围成的区域看成一个点,然后这些点之间的连边可以和原图中的边一一对应,而且相交。选好源汇点之后就可以看成是在新图上跑一个最短路,因为这条路上每一条边都对应原图上一条边,而这些边一定可以形成一个割,这样一来就是最短路的板子了,快的飞起。
#include<bits/stdc++.h>
//#define feyn
#define int long long
const int N=510;
const int M=N*N;
using namespace std;
inline void read(int &wh){
wh=0;int f=1;char w=getchar();
while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
while(w>='0'&&w<='9'){wh=wh*10+w-'0';w=getchar();}
wh*=f;return;
}
struct edge{
int t,v,next;
}e[M<<4];
int head[M],esum;
inline void add(int fr,int to,int val){
//printf("%lld %lld %lld\n",fr,to,val);
e[++esum]=(edge){to,val,head[fr]};head[fr]=esum;
}
int m,ss,tt,a[N][N],cnt;
struct node{
int pl,dis;
};
inline bool operator <(node s1,node s2){
return s2.dis<s1.dis;
}
priority_queue<node>q;
int dis[M];
signed main(){
#ifdef feyn
freopen("in.txt","r",stdin);
#endif
read(m);ss=++cnt,tt=++cnt;int in;
for(int i=1;i<=m;i++)for(int j=1;j<=m;j++)a[i][j]=++cnt;
for(int i=1;i<=m+1;i++){
for(int j=1;j<=m;j++){
read(in);
if(i==1)add(ss,a[i][j],in);
else if(i>m)add(a[i-1][j],tt,in);
else add(a[i-1][j],a[i][j],in);
}
}
for(int i=1;i<=m;i++){
for(int j=1;j<=m+1;j++){
read(in);
if(j==1)add(a[i][j],tt,in);
else if(j>m)add(ss,a[i][j-1],in);
else add(a[i][j],a[i][j-1],in);
}
}
for(int i=1;i<=m+1;i++){
for(int j=1;j<=m;j++){
read(in);
if(i==1)add(a[i][j],ss,in);
else if(i>m)add(tt,a[i-1][j],in);
else add(a[i][j],a[i-1][j],in);
}
}
for(int i=1;i<=m;i++){
for(int j=1;j<=m+1;j++){
read(in);
if(j==1)add(tt,a[i][j],in);
else if(j>m)add(a[i][j-1],ss,in);
else add(a[i][j-1],a[i][j],in);
}
}
memset(dis,0x3f,sizeof(dis));
dis[ss]=0;q.push((node){ss,0});
while(!q.empty()){
int wh=q.top().pl,nd=q.top().dis;q.pop();
if(nd>dis[wh])continue;
for(int i=head[wh],th;i;i=e[i].next){
int now=nd+e[i].v;
if(now>=dis[th=e[i].t])continue;
dis[th]=now;q.push((node){th,dis[th]});
}
}
printf("%lld",dis[tt]);
return 0;
}
一如既往,万事胜意