【NOI2010】海拔
如果点权大于,我们把它降为,贡献一定小。
可以同样推出点权下界为,就把点权压缩在到之间了。
如果在点权全为的情况下将其更改为 ~ 之间的实数,则贡献一定增大
因为原图是稳定在贡献和最小的情况下的,且每个点增加或减少所带来的贡献度变化值正负情况一定是不变的,为了保证贡献值最小,该点权一定会取到极限情况,也就是和
所以分可以暴力了。
但仔细想想,和会形成一条分界线,分便可枚举分界线。(据说是)
但分又貌似是最小割(最大流)。
但最大流强大的复杂度为O(V2E) ,而本题中,导致时间复杂度为三次方级别。
所以正解:最短路
如果要将左上角到右下角切断,可以视为有一条从左下角到右上角的贯穿矩阵的线,由多条跨越流路的线链接而成(多条割线),如果将方格作为点,跨越流路的的割作为路径,就将最小割问题转化为从左下角外围方格到右上角外围方格的最短路问题,用上堆优化的,复杂度O(n2logn)
网络流转化为对偶图求最短路
#include<bits/stdc++.h>
using namespace std;
const int N=1e6;
struct edge{int v,w,nxt;}e[N<<2];
int first[N],cnt=0;
inline void add(int u,int v,int w){
e[++cnt].w=w;e[cnt].v=v;
e[cnt].nxt=first[u];first[u]=cnt;
}
int dis[N],vis[N],s,t,n;
int dijkstra(){
priority_queue< pair<int,int> >q;
memset(dis,0x3f,sizeof(dis));
dis[s]=0;
q.push(make_pair(0,s));
while(!q.empty()){
int u=q.top().second;q.pop();
if(vis[u])continue;
vis[u]=1;
for(int i=first[u];i;i=e[i].nxt){
int v=e[i].v;
if(dis[v]>dis[u]+e[i].w){
dis[v]=dis[u]+e[i].w;
q.push(make_pair(-dis[v],v));
}
}
}
return dis[t];//不能一搜到t就结束
}
void init(){
scanf("%d",&n);
int x;s=1e6-3;t=1e6-2;
//left-->right up-->down
//方格作为点
for(int i=0;i<=n;i++)
for(int j=1;j<=n;j++){
scanf("%d",&x);
if(i==n)add(s,(i-1)*n+j,x);//
else if(!i)add(j,t,x);
else add(i*n+j,(i-1)*n+j,x);
}
for(int i=1;i<=n;i++)
for(int j=0;j<=n;j++){
scanf("%d",&x);
if(!j)add(s,(i-1)*n+j+1,x);
else if(j==n)add(i*n,t,x);
else add((i-1)*n+j,(i-1)*n+j+1,x);
}
for(int i=0;i<=n;i++)
for(int j=1;j<=n;j++){
scanf("%d",&x);
if(i==n)add((i-1)*n+j,s,x);
else if(!i)add(t,j,x);
else add((i-1)*n+j,i*n+j,x);
}
for(int i=1;i<=n;i++)
for(int j=0;j<=n;j++){
scanf("%d",&x);
if(!j)add((i-1)*n+j+1,s,x);
else if(j==n)add(t,i*n,x);
else add((i-1)*n+j+1,(i-1)*n+j,x);
}
}
int main(){
init();
printf("%d",dijkstra());
return 0;
}