好久没有写Blog,这个寒假打比赛打得挺顺利的。
正题
其实是最后不够时间了,这道题两个part我都做过。
如果没有AB两个权值那么就是平面图最小割的问题,转化为最短路就可以解决了,具体做法就是把左下角的空白看成起点,右上角的空白看成终点,如果假设要使一条边不能走相当于穿过这条边,会发现最后的决策其实是新起点到新终点的一条路径。
假若有两个值呢?发现乘积最小值的决策点大致分布在一个下凸包上,每一个决策点代表一种AB,如果不怎么会,就去做做最小乘积生成树或者画框,就可以了。
#include<bits/stdc++.h>
using namespace std;
const int N=410;
int n;
int st,ed;
struct edge{
int y,next,a,b;
}s[N*N<<1];
struct node{
int x;
long long d;
bool operator<(const node q)const{
return d>q.d;
}
};
priority_queue<node> f;
long long dis[N*N];
int A[N*N],B[N*N];
int first[N*N],len=0;
long long ans=1e18;
void ins(int x,int y,int a,int b){
s[++len]=(edge){y,first[x],a,b};first[x]=len;
}
pair<int,int> Dijkstra(int ta,int tb){
ta=-ta,tb=-tb;
while(!f.empty()) f.pop();
memset(dis,63,sizeof(dis));
f.push((node){st,0});dis[st]=0;A[st]=B[st]=0;
while(!f.empty()){
node X=f.top();f.pop();
if(X.d!=dis[X.x]) continue;
if(X.x==ed) break;
for(int i=first[X.x];i!=0;i=s[i].next) if(dis[s[i].y]>X.d+1ll*ta*s[i].a+1ll*tb*s[i].b){
dis[s[i].y]=X.d+1ll*ta*s[i].a+1ll*tb*s[i].b;
A[s[i].y]=A[X.x]+s[i].a,B[s[i].y]=B[X.x]+s[i].b;
f.push((node){s[i].y,dis[s[i].y]});
}
}
ans=min(ans,1ll*A[ed]*B[ed]);
return make_pair(A[ed],B[ed]);
}
void solve(pair<int,int> L,pair<int,int> R){
pair<int,int> now=Dijkstra(R.second-L.second,L.first-R.first);
if(1ll*(now.first-L.first)*(R.second-L.second)-1ll*(now.second-L.second)*(R.first-L.first)>0) solve(L,now),solve(now,R);
else return ;
}
int main(){
scanf("%d",&n);
int x,y;ed=(n-1)*(n-1)+1;
for(int i=1;i<n;i++){
scanf("%d %d",&x,&y);
ins(st,(i-1)*(n-1)+1,x,y);
for(int j=1;j<n-1;j++)
scanf("%d %d",&x,&y),ins((i-1)*(n-1)+j,(i-1)*(n-1)+j+1,x,y);
scanf("%d %d",&x,&y);
ins(i*(n-1),ed,x,y);
}
for(int i=1;i<n;i++) scanf("%d %d",&x,&y),ins(i,ed,x,y);
for(int i=2;i<=n-1;i++)
for(int j=1;j<n;j++)
scanf("%d %d",&x,&y),ins((i-1)*(n-1)+j,(i-2)*(n-1)+j,x,y);
for(int i=1;i<n;i++) scanf("%d %d",&x,&y),ins(st,(n-2)*(n-1)+i,x,y);
solve(Dijkstra(-1,0),Dijkstra(0,-1));
printf("%lld\n",ans);
}