AcWing 368. 银河
利用强连通分量求正环,在强连通分量内任意两点其实都是一个环,如果任意强连通分量内的任意两点的权值大于0,那么就说明这是一个正环,则无解
还需要注意的是:
差分约束的建图
N 颗恒星的亮度值总和至少有多大
求最小->求所有下界的最大->最长路 √
求最大->求所有上界的最小->最短路
最长路
dist[j] ≥ dist[t] + w[i] t+w[i]→j
T=1: A=B => A≥B B≥A B+0→A A+0→B
T=2: A<B => B≥A+1 A+1→B
T=3: A≥B => A≥B B+0→A
T=4: A>B => A≥B+1 B+1→A
T=5: A≤B => B≥A A+0→B
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100010, M = 600010;
int h[N], hs[N], e[M], ne[M], w[M], idx;
int dist[N];
int low[N], dfn[N], timep, scc_cnt, id[N];
int Size[N]; //记录连通块个数
int stk[N];
bool is_stk[N];
int top;
int n, m;
ll ans;
void add(int h[], int a, int b, int c){
e[idx] = b;
w[idx] = c;
ne[idx] = h[a];
h[a] = idx ++ ;
}
void tarjan(int u){ //找强连通分量
low[u] = dfn[u] = ++ timep;
stk[ ++ top] = u;
is_stk[u] = true;
for(int i = h[u]; ~i; i = ne[i]){
int j = e[i];
if(!dfn[j]){
tarjan(j);
low[u] = min(low[u], low[j]);
}
else if(is_stk[j]){
low[u] = min(low[u], dfn[j]);
}
}
if(low[u] == dfn[u]){
++ scc_cnt;
int y;
do{
y = stk[top -- ];
id[y] = scc_cnt;
is_stk[y] = false;
Size[scc_cnt] ++ ;
}while(u != y);
}
}
int main()
{
cin>>n>>m;
memset(h, -1, sizeof h);
memset(hs, -1, sizeof hs);
for(int i = 1; i <= n; i ++ ) add(h, 0, i, 1); //超级源点 和i有一个1的边
while(m -- ){
int t, a, b;
scanf("%d%d%d", &t, &a, &b);
//求最长路,小指向大
if(t == 1) add(h, a, b, 0), add(h, b, a, 0);
else if(t == 2) add(h, a, b, 1);
else if(t == 3) add(h, b, a, 0);
else if(t == 4) add(h, b, a, 1);
else add(h, a, b, 0);
}
tarjan(0); //因为每个点都和超级源点有边,所以直接以超级源点为起点找所有强连通分量即可
bool sucess = true;
for(int i = 0; i <= n; i ++ ){ //从超级源点开始
for(int j = h[i]; ~j; j = ne[j]){
int k = e[j];
int a = id[i], b = id[k];
if(a == b){ //如果存在环
if(w[j] > 0){ //存在正环
sucess = false;
break;
}
}
else add(hs, a, b, w[j]); //用连通块建图
}
if(!sucess) break;
}
if(!sucess) puts("-1");
else{ //该遍历求和了
for(int i = scc_cnt; i; i -- ){ //按照拓扑序
for(int j = hs[i]; ~j; j = ne[j]){ //这里遍历的是按照连通块建立的图
int k = e[j];
dist[k] = max(dist[k], dist[i] + w[j]);
}
}
for(int i = 1; i <= scc_cnt; i ++ ){
ans += Size[i] * (ll)dist[i];
}
cout<<ans<<endl;
}
return 0;
}