zoukankan      html  css  js  c++  java
  • bzoj2395 [Balkan 2011]Timeismoney(最小乘积生成树+计算几何)

    题意

    每条边有两个权值(c,t),请求出一颗生成树,使得(sum c imes sum t)最小

    题解

    为什么生成树会和计算几何扯上关系……

    对于每棵树,设(x=c,y=t),我们可以把它看成平面上的一个点,其中(sum c)为横坐标,(sum t)为纵坐标。那么题目就可以转化成求反比例函数图像上的点(k=xy)满足(k)最小

    我们先求出一棵(sum c)最小的生成树,设这个点为(A),和一棵(sum t)最小的生成树,设为(B),那么如果一个点(C)它们更优它肯定在直线(AB)的下方

    证明:因为(C)(A,B)更优,所以(A,B)肯定在这个反比例函数图像的上方。如果点(A)不在直线下方,画个图就会发现它必定有纵坐标小于(B)或者横坐标小于(A),与(A,B)的定义矛盾。故如果该点存在必定在直线(AB)下方

    那么我们需要寻找一个在直线(AB)下方且离这条直线最远的一个点(C)来更新答案,也就是说要满足(AB)叉乘(AC)最小,即最小化

    [egin{aligned} Ans &=(B.x-A.x)(C.y-A.y)-(C.x-A.x)(B.y-A.y)\ &=C.y(B.x-A.x)-C.x(B.y-A.y)-A.y(B.x-A.x)+A.x(B.y-A.y)\ end{aligned} ]

    后面两项是常数不用考虑,那么就是要最小化(C.y(B.x-A.x)-C.x(B.y-A.y))。我们把每条边的权值设为(y(B.x-A.x)-x(B.y-A.y)),跑一遍最小生成树就能得到(C)了。之后再对((A,C),(C,B))递归下去即可

    //minamoto
    #include<bits/stdc++.h>
    #define R register
    #define ll long long
    #define inf 1e18
    #define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
    #define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
    #define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
    template<class T>inline bool cmin(T&a,const T&b){return a>b?a=b,1:0;}
    using namespace std;
    char buf[1<<21],*p1=buf,*p2=buf;
    inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
    int read(){
        R int res,f=1;R char ch;
        while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
        for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
        return res*f;
    }
    const int N=205,M=10005;
    struct eg{
    	int u,v,c,t,w;
    	inline bool operator <(const eg &b)const{return w<b.w;}
    	inline void rd(){u=read()+1,v=read()+1,c=read(),t=read();}
    }e[M];
    struct node{
    	int x,y;
    	node(){}
    	node(R int xx,R int yy):x(xx),y(yy){}
    	inline node operator -(const node &b)const{return node(x-b.x,y-b.y);}
    	inline ll operator *(const node &b)const{return 1ll*x*b.y-1ll*y*b.x;}
    	inline bool operator >(const node &b)const{return 1ll*x*y==1ll*b.x*b.y?x>b.x:1ll*x*y>1ll*b.x*b.y;}
    }mc,mt,res(inf,inf);
    int n,m,u,v,fa[N];
    int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
    node kruskal(){
    	int tot=0;node P(0,0);
    	fp(i,1,n)fa[i]=i;
    	fp(i,1,m){
    		u=find(e[i].u),v=find(e[i].v);
    		if(u!=v){
    			fa[u]=v,++tot;
    			P.x+=e[i].c,P.y+=e[i].t;
    			if(tot==n-1)break;
    		}
    	}
    	return cmin(res,P),P;
    }
    void solve(node A,node B){
    	fp(i,1,m)e[i].w=1ll*e[i].t*(B.x-A.x)-1ll*e[i].c*(B.y-A.y);
    	sort(e+1,e+1+m);
    	node C=kruskal();
    	if((B-A)*(C-A)>=0)return;
    	solve(A,C),solve(C,B);
    }
    int main(){
    //	freopen("testdata.in","r",stdin);
    	n=read(),m=read();
    	fp(i,1,m)e[i].rd();
    	fp(i,1,m)e[i].w=e[i].c;
    	sort(e+1,e+1+m),mc=kruskal();
    	fp(i,1,m)e[i].w=e[i].t;
    	sort(e+1,e+1+m),mt=kruskal();
    	solve(mc,mt);
    	printf("%d %d
    ",res.x,res.y);
    	return 0;
    }
    
  • 相关阅读:
    IsEmpty函数和IsNull函数之间的区别
    JavaScript的self和this使用小结
    PHP中读写文件实现代码
    ExtJs的数据代理proxy
    php递归列出所有文件和目录的代码
    解析Extjs与php数据交互(增删查改)
    MySql 批量创建、导入实例
    MySQL 进入 导入
    小知识:批量导入数据
    js 扩展实例
  • 原文地址:https://www.cnblogs.com/bztMinamoto/p/10512465.html
Copyright © 2011-2022 走看看