题解 P3720 [AHOI2017初中组]guide

五分钟出idea,改了三遍代码原来是因为读错题了(

弱化版

更好的阅读体验?

Description

简化题意:给你一张有向图,有两个边权。对于每条边 ((u,v)),通过它的代价(也就是抱怨)就是两种边权不在 (u)(n) 的最短路上的边权的数量

Solution

先来说一下大体思路:

发现两种 GPS 眼中的边权不一样且不会相互影响,把两个 GPS 分开考虑就好了

因为要求的是 (u)(n) 的最短路,所以考虑建反图来求。
规定下面讨论是对应的边是给定的反边。

我们把每条边都带上三个权值,分别表示两个 GPS 认为的通过时间和通过这条边的抱怨数。

显然抱怨数初始为 (2),这条边符合几个 GPS 的要求就在减去几。

已经很明确了,先对两个 GPS 跑最短路,减去对抱怨数的贡献。

然后对抱怨数跑最短路即可。

这里我用的是已经死掉的 ( ext{SPFA}),题目数据应该是用脚造的,并没有卡 ( ext{SPFA}),所以跑的飞快,吸口氧是目前的最优解。


如何确定一条边((u,v)) 是否在 (u)(n) 的最短路上?

(dis_x) 表示 (x)(n) 的最短路,(e_{u,v}) 表示边 ((u,v)) 的长度,如果满足

[dis_u + e_{u,v} == dis_v ]

那么这条边就在 (v)(n) 的最短路上,对应的抱怨数减一。

下面看代码吧

Code

把三个量放进一个图里,写起来码量更短。

/*
Work by: Suzt_ilymics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define int long long
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 1e5+5;
const int MAXM = 5e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;

struct edge {
    int from, to, t[3], nxt;
}e[MAXN << 1];
int head[MAXN], num_edge = 1;

int n, m;
int dis[MAXN];
bool vis[MAXN];

int read(){
    int s = 0, f = 0;
    char ch = getchar();
    while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
    while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
    return f ? -s : s;
}

void add_edge(int from, int to, int t1, int t2) { 
    e[++num_edge].from = from, e[num_edge].to = to;
    e[num_edge].t[0] = 2, e[num_edge].t[1] = t1, e[num_edge].t[2] = t2;
    e[num_edge].nxt = head[from]; 
    head[from] = num_edge; 
}

void SPFA(int type) {
    queue<int> q;
    memset(vis, false, sizeof vis);
    memset(dis, 0x3f, sizeof dis);
    q.push(n), dis[n] = 0, vis[n] = true;
    while(!q.empty()) {
        int u = q.front(); q.pop();
        vis[u] = false;
        for(int i = head[u]; i; i = e[i].nxt) {
            int v = e[i].to;
            if(dis[v] > dis[u] + e[i].t[type]) {
                dis[v] = dis[u] + e[i].t[type];
                if(!vis[v]) q.push(v), vis[v] = true;
            }
        }
    }
}

signed main()
{
    n = read(), m = read();
    for(int i = 1, u, v, t1, t2; i <= m; ++i) {
        u = read(), v = read(), t1 = read(), t2 = read();
        add_edge(v, u, t1, t2);
    }
    for(int k = 1; k <= 2; ++k) {
        SPFA(k); // 分别对两个 GPS 跑最短路
        for(int i = 2; i <= num_edge; ++i) { // 减去最短路上的边的抱怨
            int u = e[i].from, v = e[i].to;
            if(dis[v] == dis[u] + e[i].t[k]) e[i].t[0]--;
        }
    }
//    for(int i = 2; i <= num_edge; ++i) cout<<e[i].from<<" "<<e[i].to<<" "<<e[i].t[0]<<"
";
    SPFA(0);
    printf("%lld", dis[1]);
    return 0;
}
原文地址:https://www.cnblogs.com/Silymtics/p/14824835.html