tarjan强连通分量 差分约束 dp T3 368. 银河 - AcWing题库

银河中的恒星浩如烟海,但是我们只关注那些最亮的恒星。

我们用一个正整数来表示恒星的亮度,数值越大则恒星就越亮,恒星的亮度最暗是

现在对于 颗我们关注的恒星,有 对亮度之间的相对关系已经判明。

你的任务就是求出这 颗恒星的亮度值总和至少有多大。

输入格式

第一行给出两个整数

之后 行,每行三个整数 ,表示一对恒星 之间的亮度关系。恒星的编号从 开始。

如果 ,说明 亮度相等。
如果 ,说明 的亮度小于 的亮度。
如果 ,说明 的亮度不小于 的亮度。
如果 ,说明 的亮度大于 的亮度。
如果 ,说明 的亮度不大于 的亮度。

输出格式

输出一个整数表示结果。

若无解,则输出

数据范围

输入样例:

5 7 
1 1 2 
2 3 2 
4 4 1 
3 4 5 
5 4 5 
2 3 5 
4 5 1 

输出样例:

11

思路

求亮度总和的最小值, 既然是由多个不等式约束, 那就是差分约束, 建一个不等式图求一遍最长路即可。 条件转化为:

  1. ,

接着需要找一个超级源点和绝对值, 恒星的亮度最暗是1, 那就让 号点与所有点连一条边权为 1 的边即可。

于是我们得到了一个只含有正权边且可能有正环的有向图。若采用SPFA时间复杂度是 会超时, 通过优化技巧可以过这道题, 不过还可以用tarjan缩点达到 的时间复杂度。

经过tarjan缩点处理后是一个拓扑图, 可以用 dp求解最长路。而判断正环的话, 需要在建图的时候, 若两点在一个连通分量内, 且其边权值大于0, 则该连通分量内存在正环, 无解。

代码

#include <iostream>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
#define size Size
using namespace std;
const int N = 1e5 + 10, M = 6e5 + 10;
typedef long long LL;
int h[N], hs[N], e[M], ne[M], w[M], idx;
int dfn[N], low[N], timestamp;
int stk[N], top;
bool in_stk[N];
int id[N], scc_cnt, size[N];
int n,m;
LL dist[N];
 
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] = timestamp++;
    stk[++top] = u, in_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(in_stk[j]) low[u] = min(low[u], dfn[j]);
    }
    
    if(dfn[u] == low[u])
    {
        ++scc_cnt;
        int y;
        do {
            y = stk[top--];
            in_stk[y] = false;
            id[y] = scc_cnt;
            size[scc_cnt]++;
        }while(y != u);
    }
}
 
int main()
{
    memset(h,-1,sizeof h);
    memset(hs, -1, sizeof hs);
    cin >> n >> m;
    while(m--)
    {
        int a, b, c;
        cin >> c >> a >>  b;
        if(c == 1) add(h,a,b,0), add(h,b,a,0);
        else if(c == 2) add(h,a, b, 1);
        else if(c == 3) add(h,b,a,0);
        else if(c == 4) add(h,b, a, 1);
        else add(h,a,b,0);
    }
    for(int i = 1; i <= n; i++) add(h,0,i,1);
    
    tarjan(0);
    bool flag = 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)
                {
                    flag = false;
                    break;
                }
            }
            else add(hs, a,b, w[j]);
        }
        if(!flag) break;
    }
    
    if(!flag) cout << -1 << endl;
    else 
    {
        LL res = 0;
        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++)
            res += (LL)dist[i]*size[i];
        cout << res << endl;
    }
        
    
    return 0;
}