JZOJ-senior-3621. 【BOI2011】timeismoney

3621. 【BOI2011】timeismoney

(File IO): input:timeismoney.in output:timeismoney.out
Time Limits: 2000 ms Memory Limits: 65536 KB Detailed Limits Special Judge

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

Solution

算法:最小乘积生成树

我们可以把每条边的权值描述为一个二元组(xi,yi),把生成树转化为平面内的点,然后把它投影到一个平面直角坐标系上,横坐标表示∑xi,纵坐标表示∑yi
则问题转化为求一个点,使得xy=k最小,换句话说,就是使得过这个点的反比例函数y=k/x最接近坐标轴。

因此我们需要求出所有这些点构成的凸包的左下部分,从中找一个最大的。

切入正题

1.先求出分别距x轴和y轴最近的生成树(点):A,B
实际操作可以分别按x权值和y权值做最小生成树。

2.我们可以寻找一个在AB的靠近原点的一侧且离AB最远的点C(生成树)。

怎么找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。

3.怎么求凸包的左下部分呢?分治。递归分治更新答案。分别往AC、BC靠近原点的一侧找。递归边界:该侧没有点了(即叉积大于等于零)。

Code

#include<algorithm>
#include<cstring>
#include<cstdio>
#define N 210
#define M 10010
#define ll long long
#define inf 1<<30
using namespace std;
ll i,n,m;
ll f[N],d[N][2],d1[N][2];
struct node {ll x,y,t,c,z;}a[M];
struct data {ll t,c;}mt,mc,ans;
bool cmpt(node p,node q) {return p.t<q.t;}
bool cmpc(node p,node q) {return p.c<q.c;}
bool cmpz(node p,node q) {return p.z<q.z;}
ll get(ll u)
{
    if (f[u]!=u) f[u]=get(f[u]);
    return f[u];
}
void merge(ll u,ll v)
{
    ll fu=get(u),fv=get(v);
    f[fu]=fv;
}
ll chaji(data A,data B,data C)
{
    return (B.t-A.t)*(C.c-A.c)-(B.c-A.c)*(C.t-A.t);
}
data kruskal()
{
    ll i,c=0;
    data now={0,0};
    for(i=1;i<=n;i++) f[i]=i;
    i=0; 
    while (c<n-1)
    {
        i++;
        if (get(a[i].x)!=get(a[i].y))
        {
            merge(a[i].x,a[i].y),c++;
            now.t+=a[i].t,now.c+=a[i].c;
            d1[c][0]=a[i].x,d1[c][1]=a[i].y;
        }
    }
    ll s1=ans.t*ans.c,s2=now.t*now.c;
    if (s2<s1)
    {
        ans=now;
        memcpy(d,d1,sizeof(d));
    }
    return now;
}
void getans(data A,data B)
{
    for(ll i=1;i<=m;i++) a[i].z=a[i].c*(B.t-A.t)+a[i].t*(A.c-B.c);
    sort(a+1,a+1+m,cmpz);
    data mid=kruskal();
    if (chaji(A,B,mid)>=0) return;
    getans(A,mid),getans(mid,B);
}
int main()
{
    freopen("timeismoney.in","r",stdin);
    freopen("timeismoney.out","w",stdout);
    scanf("%lld%lld",&n,&m);
    for(i=1;i<=m;i++)
    {
        scanf("%lld%lld%lld%lld",&a[i].x,&a[i].y,&a[i].t,&a[i].c);
        a[i].x++,a[i].y++;
    }
    ans.t=ans.c=inf;
    sort(a+1,a+1+m,cmpt),mt=kruskal();
    sort(a+1,a+1+m,cmpc),mc=kruskal();
    getans(mt,mc);
    printf("%lld %lld\n",ans.t,ans.c);
    for(i=1;i<=n-1;i++) printf("%lld %lld\n",d[i][0]-1,d[i][1]-1);
}

猜你喜欢

转载自blog.csdn.net/huangxinyue1017/article/details/79211035