1. 程式人生 > >【BOI2011】timeismoney (最小乘積生成樹)

【BOI2011】timeismoney (最小乘積生成樹)

Description

NetLine 公司想要給N 個城鎮提供寬頻網路。為此,需要建造一個有N -1 條鎮間寬頻連結的網路,擁有一條訊息能在這個網路上從任意鎮傳到任意鎮的性質。NetLine 已經鑑定了所有城鎮對之間能夠直接建立的連結。對於每個這樣的可能連結,他們知道建造這個連結的費用和時間。

公司對使建造總時間(連結不能同時建造)和總費用最小化都感興趣。因為他們不能決定要單獨使用哪一個標準,所以他們決定採用如下公式計算一個網路的評估值:

SumTime = 建造選擇的連結所花時間之和

SumMoney = 建造選擇的連結所花金錢之和

V = SumTime * SumMoney

選擇一些需要建造的連結,使得所建網路的評估值V 最小。

Input

輸入的第一行包含整數N——城鎮的個數和M——能夠建造的連結數。城鎮從0 到N - 1 編號。

接下來M 行中每一行含四個整數x, y, t 和c——意味著城鎮x 可以耗費t 時間及c 費用與城鎮y 建立連結。

Output

輸出的第一行為兩個數字:最優方案(那個評估值V 最小的)使用的總時間(SumTime)和總費用(SumMoney),用一個空格隔開。接下來N - 1 行描述要建造的連結。每行包含一對數字(x, y)描述一個需建造的連結(須在輸入中描述的可建造連結內)。這些數對可以按任意順序輸出。

當有多個最優解存在時,你可以輸出其中任意一個。

Sample Input

5 7

0 1 161 79

0 2 161 15

0 3 13 153

1 4 142 183

2 4 236 80

3 4 40 241

2 1 65 92

Sample Output

279 501

2 1

0 3

0 2

3 4

Data Constraint

• 1 <= N <= 200

• 1 <= M <= 10 000

• 0 <= x, y <= N - 1

• 1 <= t, c <= 255

• 一個測試點有M = N - 1

• 40% 的資料對於每個可建造連結有t = c

The Solution

題目大意

給定我們一些邊與每條邊上的兩個權值xi,yi,連成一幅無向連通圖,求在圖中找一棵最小生成樹,使得xiyi取最小值。

Analysis

就是讓我們求最小乘積生成樹

這不就擺明了是道裸體嘛QQ~~,
直接裸奔就好了

下面來普及(口胡)一下最小乘積生成樹

就拿這道題為例來說吧

我們可以把每條邊的權值描述為一個二元組(xi,yi),把生成樹轉化為平面內的點,然後把它投影到一個平面直角座標系上,橫座標表示xi,縱座標表示yi

則問題轉化為求一個點,使得xy=k最小,換句話說,就是使得過這個點的反比例函式y=kx最接近座標軸。

因此我們需要求出所有這些點構成的凸包的左下部分,從中找一個最大的。

接著我們就切入正題

1、先求出分別距x軸和y軸最近的生成樹(點):A,B
實際操作可以分別按x權值和y權值做最小生成樹。

怎麼求凸包的左下部分呢?
用分治法!!!
分治大法好!
分治大法好!
分治大法好!

2、我們可以尋找一個在AB的靠近原點的一側且離AB最遠的點C(生成樹)。遞迴分治更新答案

怎麼找C點呢?

由於C離AB最遠,所以S△ABC面積最大。

AB=B.xA.x,B.yA.y

AC=(C.xA.x,C.yA.y)

向量AB、AC的叉積(的二分之一)為S△ABC的面積(只不過叉積是有向的,是負的,所以最小化這個值,即為最大化面積)。

即最小化:

(B.xA.x)(C.yA.y)(B.yA.y)(C.xA.x)=(B.xA.x)C.y+(A.yB.y)C.xA.y(B.xA.x)+A.x(B.yA.y)/

所以將每個點的權值修改為y[i](B.xA.x)+(A.yB.y)x[i] 做最小生成樹,找到的即是C。

至於方案數的話,開個陣列記錄一下就好了。

參考CODE

#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
#define fo(i,a,b) for (int i=a;i<=b;i++)
#define fd(i,a,b) for (int i=a;i>=b;i--) 
#define N 10005
#define INF 1 << 30

using namespace std;

typedef long long ll;

struct Edge
{
    int from,to,c,t;
    ll z;
}E[N];

struct Node
{
    int x,y;
}Minx,Miny,Ans;

int Res[N][3];

int n,m;
int Dad[N];

int read(int &n)
{
    char ch = ' ';
    int q = 0, w = 1;
    for (;(ch != '-') && ((ch < '0') || (ch> '9'));ch = getchar());
    if (ch == '-') w = -1,ch = getchar();
    for (; ch >= '0' && ch <= '9';ch = getchar()) q = q * 10 + ch - 48;
    n = q * w;
    return n;
}

int Get(int x)
{
    if (Dad[x] == x) return x;
    else return Dad[x] = Get(Dad[x]);
}

bool cmp1(Edge a,Edge b)
{
    return a.t < b.t;
}
bool cmp2(Edge a,Edge b)
{
    return a.c < b.c;
}
bool cmp3(Edge a,Edge b)
{
    return a.z < b.z;
}

Node Kruskal()
{
    int tot = 0;
    Node G = {0,0};
    static int Mark[N][3];
    memset(Mark,0,sizeof(Mark));
    fo(i,1,n) Dad[i] = i;
    fo(i,1,m)
    {
        int xx = Get(E[i].from),
            yy = Get(E[i].to);
        if (xx != yy)
        {
            Dad[xx] = yy;
            tot ++;
            G.x += E[i].t;
            G.y += E[i].c;
            Mark[++ Mark[0][0]][1] = E[i].from - 1;
            Mark[Mark[0][0]][2] = E[i].to - 1; 
            if (tot == n - 1) break;
        }
    }
    ll t1 = (ll)Ans.x * Ans.y,
       t2 = (ll)G.x * G.y;
    if (t2 < t1 || (t2 == t1 && G.x < Ans.x)) 
    {
        memcpy(Res,Mark,sizeof(Mark));
        Ans = G;
    }
    return G;
}

ll Chaji(Node A,Node B,Node C)
{
    return ((ll)B.x - A.x) * ((ll)C.y - A.y ) - ((ll)B.y - A.y) * ((ll)C.x - A.x);
}

void Work(Node a,Node b)
{
    fo(i,1,m)
        E[i].z = E[i].c * (b.x - a.x) + E[i].t * (a.y - b.y);
    sort(E + 1,E + m + 1,cmp3);
    Node mid = Kruskal();
    if (Chaji(a,b,mid) >= 0) return;
    Work(a,mid);
    Work(mid,b);
}

int main()
{
    freopen("timeismoney.in","r",stdin);
    freopen("timeismoney.out","w",stdout);
    Ans.x = Ans.y = INF;
    read(n),read(m);
    fo(i,1,m) 
    {
        int u,v;
        read(u),read(v),read(E[i].t),read(E[i].c);
        E[i].from = ++ u;
        E[i].to = ++ v; 
    } 
    sort(E + 1,E + m + 1,cmp1);
    Minx = Kruskal();
    sort(E + 1,E + m + 1,cmp2);
    Miny = Kruskal();
    Work(Minx,Miny);
    printf("%d %d\n",Ans.x,Ans.y);
    fo(i,1,Res[0][0]) printf("%d %d\n",Res[i][1],Res[i][2]);
    return 0;
}