ZOJ

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 ≤ iN). 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 T indicating 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
#include<bits/stdc++.h>
using namespace std;

#define LL long long
#define cl(a,b) memset(a,b,sizeof(a))
#define pb push_back

const int maxn = 100005;
const LL inf  =1LL<<50;
const LL mod1 = 1000000007;
int n,m;

struct edge
{
    int to;
    LL cost1,cost2;
};

vector<edge> es;//把每一条边都存进向量里
vector<int> G[maxn];//记录每一条存进vector边的序号
bool used[maxn];
LL d1[maxn],d2[maxn];

void init()   //预处理
{
    es.clear();
    for(int i=0;i<maxn;i++)
    {
        G[i].clear();
        used[i]=false;
        d1[i]=d2[i]=inf;
    }
}

void addEdge(int from,int to,int w1,int w2)//连边建图过程
{
    es.push_back(edge{to,w1,w2});  //这个用法知道就好,也可以新设一个edge类型的变量来弄
    es.push_back(edge{from,w1,w2});//vector存图
    int x=es.size();//记录当前边的个数,用来记编号
    G[from].push_back(x-2);//这个是addEdge的边,表示from连的边的编号,G[from].size()代表from所连的边的条数
    G[to].push_back(x-1);//这个是addEdge的边,表示to连的边的编号,G[to].size()代表to所连的边的条数
}

struct node//这个写的是从大到小排
{
    int cur;//从起点到cer这个点
    int cost1,cost2;
    bool operator<(const node& rhs) const   //这个是为了优先队列的排序,才写的重设<的函数
    {
        if(cost1>rhs.cost1)return 1;//时间
        else if(cost1==rhs.cost1&&cost2>rhs.cost2)return 1;//花费
        else return 0;
    }
};

void dij(int s)
{
    priority_queue<node> q;
    q.push(node{s,0,0});//竟然还有这种骚操作,太强了
    d1[s]=d2[s]=0;

    while(!q.empty())
    {
        node x=q.top();
        q.pop();
        if(used[x.cur])continue;//如果已经使用过,就不进行下面的操作
        used[x.cur]=true;//否则标记这个点已经遍历过了
        for(int i=0;i<G[x.cur].size();i++)
        {
            edge e=es[G[x.cur][i]];//创建中转点
            if(d1[e.to]>d1[x.cur]+e.cost1)  //正常松弛
            {
                d1[e.to]=d1[x.cur]+e.cost1;//di[x.cur]代表从起点到要去的x.cur的时间,e.cost1代表从x.cur到e.to的时间
                d2[e.to]=e.cost2;//花费
                q.push(node{e.to,d1[e.to],d2[e.to]});//更新完之后插入队列
            }
            else if(d1[e.to]==d1[x.cur]+e.cost1&&d2[e.to]>e.cost2)  //时间相等选cost小的
            {
                d2[e.to]=e.cost2;
                q.push(node{e.to,d1[e.to],d2[e.to]});
            }
        }
    }
    LL sum=0,res=0;
    for(int i=0;i<n;i++)//最后总的时间和花费
    {
        sum+=d1[i];
res+=d2[i]; } printf("%lld %lld ",sum,res); } int main() { int T;scanf("%d",&T); while(T--) { scanf("%d%d",&n,&m); init(); for(int i=0;i<m;i++) { int x,y,c,d; scanf("%d%d%d%d",&x,&y,&c,&d); addEdge(x,y,c,d); //加双向边 } dij(0); } return 0; }
原文地址:https://www.cnblogs.com/RootVount/p/10720214.html