题解 P5590 【赛车游戏】
P5590 赛车游戏 题解
本文同步发布于
My Blog
写在前面
众所周知这是一篇题解,当然这也是一篇经验的总结。
它源自于洛谷月赛,传送门:P5590 赛车游戏
笔者写下这篇题解,一是希望自己这次的错误不要再犯,二是希望能帮助大家。
题解部分
-
题面简析
题意大致可以概括为:给你
n 个点m 条边的 一张图,你需要给每条边加上边权,使得1-n 的所有路径的长度均相等。
现在感觉问题简单多了,我们可以想到暴力地添加边权(反正边权也只有
-
解题思路
很明显上面的办法是不能拿满分的。并且我拿到本题并没有想过要打暴力。
我们假设这张图存在两个顶点
那么就有:
至于这个最值是什么,稍后再解答。
我提出了上面那个式子,那么很明显我们要求的是
好了,现在变形一下式子:
你仔细看看:
没错,就是差分约束,约束条件:
根据差分约束的经验,我们从
如果
细节实现
首先你知道知道我们只要
这个操作可以通过两遍
这样我只写了一个
void check(int x, bool *flag, int op) {
if(flag[x]) return;
flag[x] = 1;
for(int i = head[x]; i; i = Next[i]) {
if(~i & op) continue;
int y = ver[i];
check(y, flag, op);
}
}
但是
正确的写法:
void check(int x, bool *flag, int op) {
if(flag[x]) return;
flag[x] = 1;
for(int i = head[x]; i; i = Next[i]) {
if((i & 1) == op) continue;
int y = ver[i];
check(y, flag, op);
}
}
然后要注意特判
接下来看代码理解吧
#include<bits/stdc++.h>
#define ll long long
using namespace std;
/*
约束条件
d[a] + val<a,b> = d[b]
1 <= d[b] - d[a] <= 9
*/
const int N = 1e5 + 10, M = N << 2;
int head[N], Next[M], ver[M];
int edge[M], cnt = 1;
struct rec {
int x, y;
} e[M >> 1];
void add(int x, int y, int v) {
ver[++cnt] = y, edge[cnt] = v;
Next[cnt] = head[x], head[x] = cnt;
}
void Add(int x, int y) {
add(x, y, 0), add(y, x, 0);
}
bool v1[N], v2[N];
void check(int x, bool *flag, int op) {
if(flag[x]) return;
flag[x] = 1;
for(int i = head[x]; i; i = Next[i]) {
if((i & 1) == op) continue;
int y = ver[i];
check(y, flag, op);
}
}
int dis[N], tot[N];
bool v[N];
void spfa(int n) {
memset(dis, ~0x7f, sizeof dis), dis[1] = 0;
queue<int> q;
q.push(1), v[1] = 1;
while(q.size()) {
int x = q.front();
q.pop(), v[x] = 0;
for(int i = head[x]; i; i = Next[i]) {
int y = ver[i], val = edge[i];
if(dis[y] < dis[x] + val) {
dis[y] = dis[x] + val;
if(!v[y]) q.push(y), tot[y]++, v[y] = 1;
if(tot[y] > n) puts("-1"), exit(0);
}
}
}
if(dis[n] < 0) puts("-1"), exit(0);
}
void clear() {
memset(head, 0, sizeof head);
memset(Next, 0, sizeof Next);
memset(ver, 0, sizeof ver);
cnt = 0;
}
bool is[N];
int main() {
int n, m;
scanf("%d %d", &n, &m);
for(int i = 1; i <= m; i++) {
scanf("%d %d", &e[i].x, &e[i].y);
Add(e[i].x, e[i].y);
}
check(1, v1, 1), check(n, v2, 0);
for(int i = 1; i <= n; i++)
if(v1[i] && v2[i]) is[i] = 1;
is[1] = is[n] = 1;
clear();
for(int i = 1; i <= m; i++) {
if(is[e[i].x] && is[e[i].y]) {
add(e[i].x, e[i].y, 1);
add(e[i].y, e[i].x, -9);
}
}
spfa(n);
printf("%d %d\n", n, m);
for(int i = 1; i <= m; i++) {
printf("%d %d ", e[i].x, e[i].y);
if(is[e[i].x] && is[e[i].y])
printf("%d\n", dis[e[i].y] - dis[e[i].x]);
else printf("1\n");
}
return 0;
}
当时考试脑抽没写出来,然后因为那个
跪了