Problem H: AFPS
📝 题目描述
🔑 算法模块
💡 思路分析
注意到这是一个正权图,考虑这个算法的反松弛过程:
一旦存在一条某个点出发回到自己的路径(即环路),注意到这条路径的权值(其上所有边的权值和)一定为正,因此沿着这条路径一定会发生一次反松弛,而再次到达之后又会走到这条环路上,结果就是算法无法停机。
因此,如果 \(1\) 号结点能够到达一个环(也就是其所在的连通分量中存在环),答案一定是 \(0\) 。
否则,\(1\) 号结点所在连通分量一定是树,此时距离数组分为两部分:和 \(1\) 号结点在同一连通分量的点,其值为以 \(1\) 为根的树的深度(到 \(1\) 号结点的距离);其他点由于未被访问到,值为 \(-1\)。
因此找环后验证即可。一种简单的方式是直接 DFS,时间复杂度线性。
也可以在打访问标记的基础上直接使用题目给出的伪代码,注意当有解时这个算法退化为一般树上 BFS,时间复杂度也是线性。
🖥️ 代码实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 | #include<bits/stdc++.h>
using namespace std;
const int N=5e5+11;
using ll=long long;
void solve()
{
int n,m;
cin>>n>>m;
vector<vector<pair<int,int> > > G(n+3);
vector<ll> evs(m+3,0);
vector<int> vis(n+3,0);
vector<ll> d1(n+3,-1),d2(n+3,0);
d1[1]=0;
auto dfs=[&](auto &&self,int u,int eid)->bool
{
if(vis[u]) return 0;
vis[u]=1;
for(auto [v,nxid]:G[u]) if(eid!=nxid)
{
if(vis[v]) return 0;
d1[v]=d1[u]+evs[nxid];
if(!self(self,v,nxid)) return 0;
}
return 1;
};
for(int i=1,iu,iv;i<=m;++i)
{
ll ival;
cin>>iu>>iv>>ival;evs[i]=ival;
G[iu].push_back({iv,i});
G[iv].push_back({iu,i});
}
bool ok=dfs(dfs,1,-1);
for(int i=1;i<=n;++i) cin>>d2[i];
if(ok) for(int i=1;i<=n;++i) if(d1[i]^d2[i])
{
ok=0;break;
}
if(!ok) cout<<0<<'\n';else cout<<1<<'\n';
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);
int t;
cin>>t;
while(t--) solve();
return 0;
}
|
⏱️ 复杂度分析
📚 拓展