题解 P3720 [AHOI2017初中组]guide
五分钟出idea,改了三遍代码原来是因为读错题了(
Description
简化题意:给你一张有向图,有两个边权。对于每条边 \((u,v)\),通过它的代价(也就是抱怨)就是两种边权不在 \(u\) 到 \(n\) 的最短路上的边权的数量
Solution
先来说一下大体思路:
发现两种 GPS 眼中的边权不一样且不会相互影响,把两个 GPS 分开考虑就好了
因为要求的是 \(u\) 到 \(n\) 的最短路,所以考虑建反图来求。
规定下面讨论是对应的边是给定的反边。
我们把每条边都带上三个权值,分别表示两个 GPS 认为的通过时间和通过这条边的抱怨数。
显然抱怨数初始为 \(2\),这条边符合几个 GPS 的要求就在减去几。
已经很明确了,先对两个 GPS 跑最短路,减去对抱怨数的贡献。
然后对抱怨数跑最短路即可。
这里我用的是已经死掉的 \(\text{SPFA}\),题目数据应该是用脚造的,并没有卡 \(\text{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]<<"\n";
SPFA(0);
printf("%lld", dis[1]);
return 0;
}