题干:

Edward, the emperor of the Marjar Empire, wants to build some bidirectional highways so that he can reach other cities from the capital as fast as possible. Thus, he proposed the highway project.

The Marjar Empire has N cities (including the capital), indexed from 0 to N - 1 (the capital is 0) and there are M highways can be built. Building the i-th highway costs Ci dollars. It takes Di minutes to travel between city Xi and Yi on the i-th highway.

Edward wants to find a construction plan with minimal total time needed to reach other cities from the capital, i.e. the sum of minimal time needed to travel from the capital to city i (1 ≤ i ≤ N). Among all feasible plans, Edward wants to select the plan with minimal cost. Please help him to finish this task.

Input

There are multiple test cases. The first line of input contains an integer Tindicating the number of test cases. For each test case:

The first contains two integers N, M (1 ≤ N, M ≤ 105).

Then followed by M lines, each line contains four integers Xi, Yi, Di, Ci (0 ≤ Xi, Yi < N, 0 < Di, Ci < 105).

Output

For each test case, output two integers indicating the minimal total time and the minimal cost for the highway project when the total time is minimized.

Sample Input

2
4 5
0 3 1 1
0 1 1 1
0 2 10 10
2 1 1 1
2 3 1 2
4 5
0 3 1 1
0 1 1 1
0 2 10 10
2 1 2 1
2 3 1 2

Sample Output

4 3
4 4

题目大意:

给你一个无向图,每条边有一个距离c和花费d,叫你选一些边,使得点0到其他所有点的距离之和最小,其次,使总花费最小。

解题报告:

   因为总题最优解一定满足局部局部解,所以距离和最小就可以用求最短路来求解,最后统计个答案就可以。对于第二个权值的维护,可以在Dijkstra的同时贪心第二个权值,也可以先构造一个最短路子图,求其中的最短路树,也就是求最小树形图。(注意这里不是Kruskal可以解决的,,,因为其实这个新图加的是有向边、、、)

比如这个样例:

【ZOJ - 3946】Highway Project(最短路子图,维护双权值,贪心,最小树形图)_最短路

不难看出,最短路子图应该是这样的:

【ZOJ - 3946】Highway Project(最短路子图,维护双权值,贪心,最小树形图)_#include_02

而没有由上到下的那条边。

 

AC代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<string>
#include<cmath>
#include<cstring>
#define ll long long
#define pb push_back
#define pm make_pair
using namespace std;
const int MAX = 2e5 + 5;
struct Edge {
int fr,to;
ll w,c;
int ne;
} e[MAX];
struct Graph {
struct Point {
int pos;
ll c;
Point() {}
Point(int pos,ll c):pos(pos),c(c) {}
bool operator<(const Point b) const {
return c > b.c;
}
};
Edge e[MAX];
int tot = 0;//总共tot条边,编号0~(tot-1)
int head[MAX];
bool vis[MAX];
ll d[MAX],cost[MAX];
void add(int u,int v,ll w,ll c) {
e[tot].fr = u;
e[tot].to = v;
e[tot].ne = head[u];
e[tot].w = w;
e[tot].c = c;
head[u] = tot++;
}
void Dijkstra(int st) {
priority_queue<Point> pq;
memset(d,0x3f,sizeof d);
memset(cost,0x3f,sizeof cost);
memset(vis,0,sizeof vis);
d[st] = 0;
cost[st] = 0;
pq.push(Point(st,0));
while(pq.size()) {
Point cur = pq.top();
pq.pop();
if(vis[cur.pos]) continue;
vis[cur.pos] = 1;
for(int i = head[cur.pos]; ~i; i = e[i].ne) {
if(d[e[i].to] > d[cur.pos] + e[i].w) {
d[e[i].to] = d[cur.pos] + e[i].w;
cost[e[i].to] = e[i].c;
pq.push(Point(e[i].to,d[e[i].to]));
}
else if(d[e[i].to] == d[cur.pos]+e[i].w) {
if(cost[e[i].to] > e[i].c) {
cost[e[i].to] = e[i].c;
pq.push(Point(e[i].to,d[e[i].to]));
}
}
}
}
}
void id_add(int u,int v,ll w,ll c) {
e[tot].fr = u;
e[tot].to = v;
e[tot].ne = head[u];
e[tot].w = w;
e[tot].c = c;
head[u] = tot++;
}
void init(int n) {
tot = 0;
for(int i = 0; i<=n; i++) head[i] = -1;
}
} G1;
int main() {
int n,m;
int u,v;
ll w,c;
int t;
cin>>t;
while(t--) {
scanf("%d%d",&n,&m);
G1.init(n);
for(int i = 1; i<=m; i++) {
scanf("%d%d%lld%lld",&u,&v,&w,&c);
u++,v++;
G1.add(u,v,w,c);
G1.add(v,u,w,c);
}
G1.Dijkstra(1);
ll ans1 = 0,ans2 = 0;
for(int i = 1; i<=n; i++) ans1 += G1.d[i],ans2 += G1.cost[i];
printf("%lld %lld\n",ans1,ans2);
}
return 0 ;
}