ABC280F 题解

思考

显然,我们把图分成诺干个联通块.

对于一个询问\(x,y\),诺\(x,y\)不在同一个联通块中.则不存在路径,输出nan

当\(x,y\)在同一个联通块内呢?

诺这个联通块内存在正权环,那么意味着可以通过绕这个环使得我们的权值无限大,那么输出inf

我们考虑什么情况下会出现正环,发现,只要一个环的权值不为\(0\),那就存在正环,哪怕他是负环,那么反着走负环就是正环了.

那么我们考虑没有正环的情况

我们画一个拓扑图,为了方便表示,我们把这个图当成有向图.方向与权值为正时的方向有关.

显然,由于没有正环,所以这个图是一个有向无环图.

我们考虑如何计算两个点的最短路.

我们举个例子研究一下

image

然后,我们发现由于有性质,就是整个图上所有的环的权值和都是\(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\),

我们从环上考虑.

对于环

image

显然,整个环的权值和为\(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;
}
posted @ 2026-10-02 14:18  txp2025  阅读(8)  评论(0)    收藏  举报