ABC280F 题解
思考
显然,我们把图分成诺干个联通块.
对于一个询问\(x,y\),诺\(x,y\)不在同一个联通块中.则不存在路径,输出nan
当\(x,y\)在同一个联通块内呢?
诺这个联通块内存在正权环,那么意味着可以通过绕这个环使得我们的权值无限大,那么输出inf
我们考虑什么情况下会出现正环,发现,只要一个环的权值不为\(0\),那就存在正环,哪怕他是负环,那么反着走负环就是正环了.
那么我们考虑没有正环的情况
我们画一个拓扑图,为了方便表示,我们把这个图当成有向图.方向与权值为正时的方向有关.
显然,由于没有正环,所以这个图是一个有向无环图.
我们考虑如何计算两个点的最短路.
我们举个例子研究一下

然后,我们发现由于有性质,就是整个图上所有的环的权值和都是\(0\),那么对于任意的\(s \to t\)的路径,路径的权值相等.
这使得我们无需关心路径是否最优.
那么我们随便找一个点\(r\),然后就可以\(dis(s,t)=dis(S,r)=dis(r,t)\)计算\(s \to t\)的路径了
实现细节
我们考虑如何实现判断是否存在非\(0\)环.
常规做法是\(SPFA\) 但是这会导致复杂度退化为\(O(nm)\),这无法接受
但是由于有任何环的权值和为\(0\),我们可以跑一遍\(bfs\),诺存在访问一个点的时候,这个点已经有的权值与我们现在计算出来的权值不一样.那么就意味着存在非\(0\)环.
那么判断非\(0\)环的复杂度就变成\(O(n)\)了.
然后你预处理单源点\(dis\)的复杂度也是\(O(n)\)
难点分析
-
从不能有正环推理出所有环的权值为\(0\)
-
从所有环的权值为\(0\)想到\(s \to t\)的任意路径的权值相同
我们详解这是怎么想到的
对于从不能有正环推理出所有环的权值为\(0\)
由于对于边\(u \to v\),总存在\(v \to u\)且权值为相反数.
那么对于路径,也存在\(s \to t\)的权值为\(w\),那存在\(t \to s\)的权值为\(-w\)
如果\(s=t\),那么我们得到了,诺存在环的全职为\(w\),那么总存在环的权值为\(-w\).
那么不能存在正环,自然也就不能存在负环.
那么就只能存在\(0\)环了.
对于 \(s \to t\)的任意路径的权值相同
我先提一嘴,\(s \to t\)不是\(t \to s\),
我们从环上考虑.
对于环

显然,整个环的权值和为\(0\).
然后蓝色的地方是我们现在的路径. 权值为\(w\)
那么整个环剩下的路径的权值就是\(0-w=-w\)
但是由于方向反了,所以权值也要变为相反数,所以剩下的路径\(s \to t\)的权值也是\(w\). 也就是红色部分.
考虑链,显然,对于\(s \to t\)都在链上,任何路径的权值相等. 任何不是直接\(s \to t\)的路径多于的边的权值,会在折返的时候抵消.
你给链和环的情况结合一下就得到了这个性质
进步
这告诉我们在做题的时候,可以手动构造环,链,菊花图等特殊结构来挖掘性质.
而不是仅仅从题目本身的模型出发
代码
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
ll read() {
ll ret=0,f=1;
char ch;
ch=getchar();
while(ch<'0' || ch>'9') {
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0' && ch<='9') {
ret=ret*10+(ch-'0');
ch=getchar();
}
return ret*f;
}
const ll maxn=1e5+5;
const ll inf=1e18;
struct Edge{
bool p; //正反方向 正=0 反=1
ll v,w,nxt;
};
struct node{
ll u,w;
bool operator<(const node &other)const{
return w<other.w;
}
};
struct Task{
ll u,v,scc,id;
};
bool tag[maxn]; //判断每个scc是否预处理过元素
bool large[maxn]; //判断每个scc是否存在正权环
ll ans[maxn];
Task t[maxn];
Edge e[maxn<<1];
ll cnt[maxn]; //记录每个节点的更新次数
ll f[maxn],sz[maxn]; //联通块和联通块大小
ll dis[maxn]; //最短路
ll head[maxn],ecur;
ll from[maxn],to[maxn];
ll n,m,q;
void add_edge(ll u,ll v,ll w,bool p) {
++ecur;
e[ecur]={p,v,w,head[u]};
head[u]=ecur;
}
ll find(ll x) {
return f[x]==x?x:f[x]=find(f[x]);
}
void merge(ll x,ll y) {
ll rx=find(x),ry=find(y);
if(ry==rx) {
return;
}
if(sz[rx]<sz[ry])
swap(rx,ry);
sz[rx]+=sz[ry];
f[ry]=rx;
return;
}
bool vis[maxn];
void work(ll root) {
queue<ll> que;
vis[root] = true;
from[root] = 0;
que.push(root);
while(!que.empty()) {
ll u = que.front();
que.pop();
for(ll i = head[u]; i; i = e[i].nxt) {
ll v = e[i].v;
ll w = e[i].w;
if(!vis[v]) {
vis[v] = true;
from[v] = from[u] - w;
que.push(v);
}
else if(from[v] != from[u] - w) {
large[root] = true;
return;
}
}
}
}
`
int main() {
// freopen("input.txt","r",stdin);
n=read();
m=read();
q=read();
fill(from+1,from+1+n,-inf);
fill(to+1,to+1+n,-inf);
fill(sz+1,sz+1+n,1);
iota(f+1,f+1+n,1);
for(ll i=1;i<=m;++i) {
ll a,b,w;
a=read();
b=read();
w=read();
add_edge(a,b,w,0);
add_edge(b,a,-w,1);
merge(a,b);
}
for(ll i=1;i<=q;++i) {
ll u,v;
u=read();
v=read();
if(find(u)==find(v)) {
t[i]={u,v,find(u),i};
}else{
t[i]={u,v,0,i};
}
}
sort(t+1,t+1+q,[](const Task&a,const Task&b){return a.scc<b.scc;});
for(ll i=1;i<=q;++i) {
if(!t[i].scc) {
ans[t[i].id]=-inf;
continue;
}
if(!tag[t[i].scc]) {
work(t[i].scc);
tag[t[i].scc] = 1;
}
if(large[t[i].scc]) {
ans[t[i].id]=inf;
continue;
}
ans[t[i].id]=from[t[i].u]-from[t[i].v];
}
for(ll i=1;i<=q;++i) {
if(ans[i]==inf) {
puts("inf");
continue;
}
if(ans[i]==-inf) {
puts("nan");
continue;
}
printf("%lld\n",ans[i]);
}
return 0;
}

浙公网安备 33010602011771号