「HNOI 2014」 画框

题目链接

戳我

\(Solution\)

这一题很像最小乘积生成树.只是把\(kruskal\)变为了\(km\)/费用流

现在来讲一讲最小乘积生成树.首先将\(\sum a_i\)\(\sum b_i\)看为坐标轴上的点\((x,y)\)

\(step 1:\) 首先找出离\(x\)最近的点和离\(y\)最近的点
\(step 2:\) 找出距离\(A,B\)最远的\(C\)(\(C\)要在\(A,B\)左边)
\(step 3:\) 递归处理\(AC\)\(CB\),直到找不到\(C\)为止

现在来讲一下怎么求\(C\),我们可以通过向量的叉积来算.

因为\(C\)要离\(AB\)最远等价于\(S_{\Delta ABC}\)最大即:\(\overrightarrow{A C}*\overrightarrow{A B}\)最小

\[\overrightarrow{A C}*\overrightarrow{A B} \]

\[=(X_B-X_A)*(Y_C-Y_A)-(Y_B-Y_A)*(X_C-X_A) \]

\[=(X_B-X_A)*Y_C+(Y_A-Y_B)*X_C+ans \]

ans为一个常数,不用管他.

然后我们直接将权值/费用改为\((X_B-X_A)*b+(Y_A-Y_B)*a\)在跑一遍\(km/\)费用流即可

\(luogu\)没过T了,\(bzoj\)过了

\(Code\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int read() {
    int x=0,f=1;
    char c=getchar();
    while(c<'0'||c>'9') f=(c=='-')?-1:1,c=getchar();
    while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x*f;
}
struct node {
    int x,y;
    bool operator == (const node &z)const {
        return x==z.x&&y==z.y;
    }
};
node operator - (const node & a , const node & b) {
    return (node) {
        a.x-b.x,a.y-b.y
    };
}
int calc(node x, node y) {
    return (x.x * y.y) - (x.y * y.x);
}
node minx;
struct node1 {
    int to,next,v,w;
} a[1000001];
int dis[100001],f[100001],pre[100001],fa[100001],s,t,n,m,head[100001],cnt,x,y,z,c,A[100][100],B[100][100];
void add(int x,int y,int c,int v) {
    a[++cnt].to=y,a[cnt].next=head[x],a[cnt].v=c,a[cnt].w=v,head[x]=cnt;
    a[++cnt].to=x,a[cnt].next=head[y],a[cnt].v=0,a[cnt].w=-v,head[y]=cnt;
}
queue < int > q;
int spfa() {
    q.push(s);
    for(int i=s; i<=t; i++)
        dis[i]=2147483647,f[i]=0;
    f[s]=1,dis[s]=0;
    int inf=dis[s+1];
    while(!q.empty()) {
        int now=q.front();
        q.pop(),f[now]=0;
        for(int i=head[now]; i; i=a[i].next) {
            int v=a[i].to;
            if(dis[v]>dis[now]+a[i].w&&a[i].v) {
                dis[v]=dis[now]+a[i].w,pre[v]=i,fa[v]=now;
                if(!f[v])
                    f[v]=1,q.push(v);
            }
        }
    }
    if(dis[t]!=inf)
        return 1;
    return 0;
}
int ans1,ans,minn=2147483647;
void answer() {
    while(spfa()) {
        int minx=2147483647;
        for(int i=t; i!=s; i=fa[i])
            minx=min(minx,a[pre[i]].v);
        ans+=minx,ans1+=dis[t]*minx;
        for(int i=t; i!=s; i=fa[i])
            a[pre[i]].v-=minx,(pre[i]%2)?a[pre[i]+1].v+=minx:a[pre[i]-1].v+=minx;
    }
}
node work(int fx,int fy) {
    cnt=0,s=0,t=2*n+1;
    for(int i=s; i<=t; i++)
        head[i]=0;
    for(int i=1; i<=n; i++) {
        add(s,i,1,0),add(i+n,t,1,0);
        for(int j=1; j<=n; j++)
            add(i,j+n,1,A[i][j]*fx+B[i][j]*fy);
    }
    answer();
    int x=0,y=0;
    for(int i=1; i<=n; i++)
        for(int j=head[i]; j; j=a[j].next)
            if(!a[j].v&&a[j].to)
                x+=A[i][a[j].to-n],y+=B[i][a[j].to-n];
    node ans2;
    ans2.x=x,ans2.y=y;
    if(x*y<minn)
        minn=x*y;
    return ans2;
}
void devide(node l,node r) {
    node mid=work(l.y-r.y,r.x-l.x);
    if(l==mid||mid==r) return ;
    devide(l,mid),devide(mid,r);
}
main() {
    int T=read();
    node l,r;
    while(T--) {
        n=read(),minn=2147483647;
        for(int i=1; i<=n; i++)
            for(int j=1; j<=n; j++)
                A[i][j]=read();
        for(int i=1; i<=n; i++)
            for(int j=1; j<=n; j++)
                B[i][j]=read();
        l=work(1,0),r=work(0,1);
        devide(l,r);
        printf("%d\n",minn);
    }
}
posted @ 2019-02-20 08:09  撤云  阅读(184)  评论(0编辑  收藏  举报
……