专栏文章

题解:AT_abc280_f [ABC280F] Pay or Receive

AT_abc280_f题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@mip8dqbk
此快照首次捕获于
2025/12/03 07:50
3 个月前
此快照最后确认于
2025/12/03 07:50
3 个月前
查看原文

题目大意

有一张有向图,其中 n\text{n} 个顶点,2m\text{2m} 条边。第 i\text{i} 条边连接 aia_{i}bib_{i},其中从 aia_{i}bib_{i} 的权值为 cic_{i},从 bib_{i}aia_{i} 的权值为 ci-c_{i}。给出 QQ 个问题:
  • XiX_{i}YiY_{i} 时的最大得分。
  • 如果 XiX_{i}YiY_{i} 不在一个子图里,输出 nan
  • 如果 XiX_{i}YiY_{i} 的分数可以是无限,输出 inf
注:本题解的子图可以理解为强连通分量。

思路

分析

这是‌最长路‌问题,根据题意,输出有 33 种情况。
  • 第一种情况‌:需 aia_ibib_i 在同一子图中。
  • 第二种情况‌:aia_ibib_i 不在同一子图,输出 nan
  • 第三种情况‌:子图存在正环,输出 inf

解法

首先我们需要了解图的两个知识。
  1. 如下图,我们举个例子,我们问从 11 号点到 66 号点的最大距离是多少?很容易看出 11 号点到 66 号有两条路径,通过观察,我们易得,只要到终点的其中两条路径不相等,则这个子图是正环,也就是子图内的所有点的到图内任意点的距离都是可以无限刷分的。
正环示例图
  1. 直接暴力搜图的肯定是不行的,所以我想到的一个类似前缀和的方法。还是那个图,如果我们问 44 号点到 55 号点的最长路,我们就可以用 1155 号点的最长路减 1144 号点的最长路,即 l[y]l[x]l[y] - l[x] 其中 l[]l[]11xx 点的最长路。
按上述枚举一遍边,最后输出答案。

代码

CPP
#include<bits/stdc++.h>
using namespace std;
// 表示从一个节点到另一个节点的有向边
struct node 
{	
    long long to, z;	 // to: 目标节点, z: 边的权值 
};

vector<node> v[1000009];
long long n, m, q, x, y, t;
long long l[1000009];  // l[]: 节点 i 在当前子图中的基准得分
long long j[1000009];  // j[]: 节点 i 所属的子图编号
long long cnt;         // cnt: 当前子图的编号
long long p[1000009];  // p[]: 标记子图 i 是否存在正环(可获得无限分数)
bool f[1000009];  

void dfs(long long dis)
{
    j[dis] = cnt;  
    f[dis] = true;  
    long long len = v[dis].size();  
    for(long long i = 0; i < len; i++)  // 遍历所有边
    {
        long long u = v[dis][i].to;  
        if(f[u] == false)  // 如果目标节点未被访问
        {
            l[u] = l[dis] + v[dis][i].z;  
            dfs(u);  
        }
        else if(l[u] != l[dis] + v[dis][i].z)  // 说明存在正环
            p[cnt] = 1;  // 标记当前子图存在正环
    }
    return ;
}

int main()
{
    scanf("%lld%lld%lld", &n, &m, &q); 
    for(long long i = 1; i <= m; i++) 
    {
        scanf("%lld%lld%lld", &x, &y, &t);
        node p; 
        p.to = y, p.z = t;  // 设置正向边(x->y,得分为 t)
        v[x].push_back(p); 
        p.to = x, p.z = -t;  // 设置反向边(y->x,得分为 -t)
        v[y].push_back(p); 
    }
    
    for(long long i = 1; i <= n; i++)  // 遍历所有节点,处理各个子图
        if(f[i] == false)  // 如果节点 i 未被访问(属于新的子图)
        {    
            cnt++;  // 子图编号加 1
            dfs(i);  // 从节点 i 开始深度优先搜索
        }
    
    while(q--)
    {
        scanf("%lld%lld", &x, &y); 
        if(j[x] != j[y])  // 如果两个节点不在同一个子图中
            printf("nan\n");
        else if(p[j[x]])  // 存在正环
            printf("inf\n");
        else printf("%lld\n", l[y] - l[x]); 
    }
    return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...