• COJ 0248 HDNOIP201408生成树


    HDNOIP201408生成树
    难度级别: A; 编程语言:不限;运行时间限制:5000ms; 运行空间限制:262144KB; 代码长度限制:2000000B
    试题描述

    输入
    第一行包括两个整数V,E,表示图的度数和图的边数接下来E行,每行包括4 个整数v1,v2,a(v1,v2), b(v1,v2),表示图中的v1,v2 两点之间有一条权值为a(v1,v2), b(v1,v2)的边。顶点的下标从0 开始标记,即0<=v1,v2<V。 
    输出
    仅一行,包含一个整数:F(T)的最小值。
    输入示例
    3 4
    1 2 1 1
    2 0 1 1
    0 1 1 1
    0 2 2 3
    输出示例
    4
    其他说明
    对于20%的数据,V<=10,E<=20
    对于50%的数据,V<=50,E<=1000 
    对于100%的数据,V<=300,E<=10000,1 ≤a_((v1,v2)), b_((v1,v2))≤ 1000

    题解:最小乘积生成树的板子。

     设每个点有x,y两个权值,求一棵生成树,使得sigma(x[i])*sigma(y[i])最小。

    设每棵生成树为坐标系上的一个点,sigma(x[i])为横坐标,sigma(y[i])为纵坐标。则问题转化为求一个点,使得xy=k最小。即,使过这个点的反比例函数y=k/x最接近坐标轴。

    Step1:求得分别距x轴和y轴最近的生成树(点):A、B(分别按x权值和y权值做最小生成树即可)。

    Step2:寻找一个在AB的靠近原点一侧的且离AB最远的生成树C,试图更新答案。

    【怎么找????

    ——由于C离AB最远,所以S△ABC面积最大。

    向量AB=(B.x - A.x , B.y - A.y)

    向量AC= (C.x - A.x , C.y - A.y)

    向量AB、AC的叉积(的二分之一)为S△ABC的面积(只不过叉积是有向的,是负的,所以最小化这个值,即为最大化面积)。

    最小化:(B.x-A.x)*(C.y-A.y)-(B.y-A.y)*(C.x-A.x)

    =(B.x-A.x)*C.y+(A.y-B.y)*C.x  -  A.y*(B.x-A.x)+A.x*(B.y-A.y)/*粗体为常数,不要管*/

    所以将每个点的权值修改为 y[i]*(B.x-A.x)+(A.y-B.y)*x[i] 做最小生成树,找到的即是C。】

    Step3:递归地分别往AC、BC靠近原点的一侧找。递归边界:该侧没有点了(即叉积大于等于零)。——The Solution By AutSky_JadeK(From SDOI).http://www.cnblogs.com/autsky-jadek/.

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cmath>
     4 #include<algorithm>
     5 #include<queue>
     6 #include<cstring>
     7 #define PAU putchar(' ')
     8 #define ENT putchar('
    ')
     9 using namespace std;
    10 typedef long long ll;
    11 const int maxn=300+10;
    12 const int maxm=20000+10;
    13 const int inf=1<<28;
    14 inline long long read(){
    15     long long x=0,sig=1;char ch=getchar();
    16     while(!isdigit(ch)){if(ch=='-') sig=-1;ch=getchar();}
    17     while(isdigit(ch)) x=10*x+ch-'0',ch=getchar();
    18     return x*=sig;
    19 }
    20 inline void write(int x){
    21     if(x==0){putchar('0');return;}if(x<0) putchar('-'),x=-x;
    22     int len=0,buf[15];while(x) buf[len++]=x%10,x/=10;
    23     for(int i=len-1;i>=0;i--) putchar(buf[i]+'0');return;
    24 }
    25 struct point{ll x,y;}ans;
    26 point operator-(const point&a,const point&b){return(point){a.x-b.x,a.y-b.y};}
    27 ll operator*(point a,point b){return a.x*b.y-a.y*b.x;}
    28 bool operator<(point a,point b){return a.x*a.y<b.x*b.y||a.x*a.y==b.x*b.y&&a.x<b.x;}
    29 struct edge{int u,v,x,y;ll w;}e[maxm];
    30 bool operator<(edge a,edge b){return a.w<b.w;}
    31 int n,m,fa[maxn];
    32 int findset(int x){return x==fa[x]?x:fa[x]=findset(fa[x]);}
    33 void setinit(){for(int i=0;i<n;i++)fa[i]=i;return;}
    34 point kruscal(){
    35     point tmp=(point){0,0};setinit(),sort(e,e+m);
    36     for(int i=0;i<m;i++){
    37         int u=findset(e[i].u),v=findset(e[i].v);
    38         if(u!=v){
    39             fa[u]=v;
    40             tmp.x+=e[i].x;
    41             tmp.y+=e[i].y;
    42         }
    43     }if(tmp<ans)ans=tmp;return tmp;
    44 }
    45 void solve(point a,point b){
    46     for(int i=0;i<m;i++)e[i].w=(b.x-a.x)*e[i].y+(a.y-b.y)*e[i].x;
    47     point c=kruscal();if((a-c)*(b-c)<0)solve(a,c),solve(c,b);return;
    48 }
    49 void init(){
    50     n=read(),m=read();
    51     for(int i=0;i<m;i++){
    52         e[i].u=read(),e[i].v=read();
    53         e[i].x=read(),e[i].y=read();
    54     }ans.x=ans.y=inf;
    55     return;
    56 }
    57 void work(){
    58     for(int i=0;i<m;i++)e[i].w=e[i].x;
    59     point a=kruscal();
    60     for(int i=0;i<m;i++)e[i].w=e[i].y;
    61     point b=kruscal();
    62     solve(a,b);
    63     return;
    64 }
    65 void print(){
    66     write(ans.x*ans.y);
    67     return;
    68 }
    69 int main(){init();work();print();return 0;}
  • 相关阅读:
    C# 正则 获取 Img Src路径
    .NET动态加载用户控件并传值的方法
    ViewState压缩技术
    BookStrap中关于button和图片的注意点
    在idea中使用Git
    了解Git的使用
    javascript-----DOM文档对象模型
    浅谈java集合
    javaI/O流
    二进制和十进制的转换
  • 原文地址:https://www.cnblogs.com/chxer/p/4642161.html
Copyright © 2020-2023  润新知