题解 CF - 101986 F Pizza Delivery (最短路+DAG必经点)
题目链接: https://codeforces.com/gym/101986
题意:
给你 N N N个点, M M M条边的有向图,无重边自环,每条边都有边权 w i w_i wi,设置起点为1,终点为2,现在要求对每条边询问一下:
如果将这条边的方向取反,即原来是 u → v u \rarr v u→v现在修改为 v → u v\rarr u v→u 从 s → t s \rarr t s→t 的最短路有无变化
如果最短路增加了输出 “SAD”,不变输出"SOSO",减少了输出 “HAPPY”
数据范围: 2 ≤ N , M ≤ 1 0 5 , 2 ≤ w i ≤ 1 0 5 , 2 \le N,M \le 10^5 , 2 \le w_i \le 10^5, 2≤N,M≤105,2≤wi≤105,
思路:
题目要问与最短路的关系,那显然,我们得将最短路的图预处理出来,这是一个最短路DAG。
判断经过这条边 u → v u\rarr v u→v的路径长度变化,我们可以知道,现在的长度是 d i s [ s ] [ v ] + d i s [ u ] [ t ] + w dis[s][v]+dis[u][t]+w dis[s][v]+dis[u][t]+w ,当然这条路径可能会经过原先 u → v u\rarr v u→v 的这条边,不过没有影响,因为,如果经过了这条边,一定会使得长度变得更长。
所以当结果为"HAPPY"的时候非常容易判断。
但是"SAD"和"SOSO",就比较复杂了,因为如果这条边是在最短路DAG上的边,就有很多情况:
- 最短路DAG全都经过这条边,所以经过这条边的长度增加了最短路也增加
- 最短路DAG可以不经过这条边,所以经过这条边的长度增加了,最短路不变
这显然就是最短路中的必经边问题。我们可以使用支配树来求,也可以使用DP来求。
这里使用了DP。
- 在原图中按照拓扑序进行动态规划,求出起点 S S S到图中每个点 x x x的路径条数 f s [ x ] fs[x] fs[x]。
- 在反图上再次按照拓扑序进行动态规划,求出每个点 x x x到终点 T T T的路径条数 f t [ x ] ft[x] ft[x]。
显然,
f
s
[
t
]
fs[t]
fs[t]表示从
S
S
S到
T
T
T的路径总条数。根据乘法原理:
1.对于一条有向边
(
x
,
y
)
(x,y)
(x,y),若
f
s
[
x
]
∗
f
t
[
y
]
=
=
f
s
[
T
]
fs[x]∗ft[y]==fs[T]
fs[x]∗ft[y]==fs[T],则
(
x
,
y
)
(x,y)
(x,y)是有
D
A
G
DAG
DAG从
S
S
S到
T
T
T的必经边。
2.对于一个点,若
f
s
[
x
]
∗
f
t
[
x
]
=
=
f
s
[
T
]
fs[x]∗ft[x]==fs[T]
fs[x]∗ft[x]==fs[T],则
x
x
x是
D
A
G
DAG
DAG从
S
S
S到
T
T
T的必经点。
路径条数是一个指数级别的整数,通常超过了32位或64位整数的表示范围。受 H a s h Hash Hash思想的启发,我们可以把路径条数对一个大指数取模后再保存到 f s fs fs与 f t ft ft数组中。这样带来的后果是有较小的概率会产生误判。保险起见,若题目时限宽松,我们多选取几个质数,分别作为模数进行计算。
代码
#include <bits/stdc++.h>
using namespace std;
#define rep(i,j,k) for(ll i = (ll)j;i <= (ll)k;i ++)
#define debug(x) cerr<<#x<<":"<<x<<endl
#define pb push_back
typedef long long ll;
typedef pair<ll,ll> pi;
const ll MAXN = (ll)1e5+7;
const ll INF = (ll)1e18+7;
const ll MOD = (ll)1e9+7;
struct Node{
ll u,v,w;
Node(ll u=0,ll v=0,ll w=0):u(u),v(v),w(w){}
}pro[MAXN];
vector<pi>G[3][MAXN];
vector<ll>V[2][MAXN];
ll dis[3][MAXN],dp[2][MAXN],deg[2][MAXN];
ll N,M,s,t;
priority_queue<pi> qu;
void djs(ll op) {
while (!qu.empty()) qu.pop();
rep(i,0,N) dis[op][i] = INF;
dis[op][op] = 0;
qu.push(make_pair(0,op));
while (!qu.empty()) {
pi k = qu.top();qu.pop();
ll now = k.second;
ll cos = -k.first;
for(pi to:G[op][now]) {
ll v = to.second;
ll tmpCos = to.first;
if (dis[op][v] > cos+tmpCos) {
dis[op][v] = cos+tmpCos;
qu.push(make_pair(-dis[op][v],v));
}
}
}
}
queue<int> que;
void bfs(ll op) {
while (!que.empty()) que.pop();
rep(i,1,N) {
if (deg[op][i]==0) {
que.push(i);
dp[op][i] = 1;
}
}
while (!que.empty()) {
ll k = que.front();que.pop();
for(ll v:V[op][k]) {
dp[op][v] = (dp[op][v]+dp[op][k])%MOD;
if (--deg[op][v]==0) {
que.push(v);
}
}
}
}
int main()
{
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
cin >> N >> M;
rep(i,1,M) {
ll u,v,w;
cin >> u >> v >> w;
G[1][u].pb(make_pair(w,v));
G[2][v].pb(make_pair(w,u));
pro[i] = Node(u,v,w);
}
s = 1,t = 2;
djs(1);
djs(2);
rep(i,1,N) {
for(pi to:G[1][i]) {
ll u = i,v = to.second,w = to.first;
if (dis[s][u]+w+dis[t][v]==dis[s][t]) {
V[0][u].pb(v);
V[1][v].pb(u);
deg[0][v] ++;
deg[1][u] ++;
}
}
}
bfs(0);
bfs(1);
rep(i,1,M) {
ll u = pro[i].u,v = pro[i].v,w = pro[i].w;
if (dis[s][v]+dis[t][u]+w < dis[s][t]) {
cout << "HAPPY" << endl;
}else if (dis[s][v]+dis[t][u]+w == dis[s][t]) {
cout << "SOSO" << endl;
}else {
if (dis[s][u]+w+dis[t][v]==dis[s][t]) {
if (dp[0][u]*dp[1][v]%MOD == dp[0][t]) {
cout << "SAD" << endl;
}else {
cout << "SOSO" << endl;
}
}else {
cout << "SOSO" << endl;
}
}
}
}