AcWing 368 差分约束系统 + Tarjan SCC + 拓扑序 DP

题意

传送门 AcWing 368

题解

将各类关系转化为差分约束系统,求解各节点亮度的最小值,将其转化为求解最长路。

d d d 代表节点的亮度。 T = 1 T=1 T=1 时, d [ A ] − d [ B ] ≥ 0 , d [ B ] − d [ A ] ≥ 0 d[A]-d[B]\geq 0,d[B]-d[A]\geq 0 d[A]d[B]0,d[B]d[A]0 T = 2 T=2 T=2 时, d [ B ] − d [ A ] ≥ 1 d[B]-d[A]\geq 1 d[B]d[A]1 T = 3 T=3 T=3 时, d [ A ] − d [ B ] ≥ 0 d[A]-d[B]\geq 0 d[A]d[B]0 T = 4 T=4 T=4 时, d [ A ] − d [ B ] ≥ 1 d[A]-d[B]\geq 1 d[A]d[B]1 T = 5 T=5 T=5 时, d [ B ] − d [ A ] ≥ 0 d[B]-d[A]\geq 0 d[B]d[A]0。为保证亮度最暗为 1 1 1,设 d [ 0 ] = 0 d[0]=0 d[0]=0,加上约束条件 ∀ A , d [ A ] − d [ 0 ] ≥ 1 \forall A,d[A]-d[0]\geq 1 A,d[A]d[0]1

S P F A SPFA SPFA 求解最长路 O ( N M ) O(NM) O(NM),难以胜任。观察到差分约束系统中边权非负,若图中存在环,则环上边权和一定为 0 0 0;否则,将出现 x > x x>x x>x 这类无解情况。

T a r j a n Tarjan Tarjan 算法求解 S C C SCC SCC,判断所有强连通分量内部是否存在权值非零的边。若差分约束系统有解,则使用拓扑序 D P DP DP 求解最长路。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 100005, maxm = 3 * 100005;
int N, M, fg, num, low[maxn], dfn[maxn];
int top, st[maxn], nr, rec[maxn];
int scc, sc[maxn], deg[maxn], dp[maxn];
int tot, head[maxn], to[maxm], nxt[maxm], cost[maxm];
int tot_c, hc[maxn], tc[maxm], nc[maxm], cc[maxm];
bool vs[maxn], in[maxn];

inline void add(int x, int y, int z) {
    
     to[++tot] = y, cost[tot] = z, nxt[tot] = head[x], head[x] = tot; }

inline void add_c(int x, int y, int z) {
    
     tc[++tot_c] = y, cc[tot_c] = z, nc[tot_c] = hc[x], hc[x] = tot_c; }

void dfs(int x)
{
    
    
    vs[x] = 1;
    for (int i = head[x]; i; i = nxt[i])
    {
    
    
        int y = to[i], z = cost[i];
        if (sc[y] != scc)
            continue;
        if (z)
            fg = 0;
        else if (!vs[y])
            dfs(y);
    }
}

void tarjan(int x)
{
    
    
    low[x] = dfn[x] = ++num;
    st[++top] = x, in[x] = 1;
    for (int i = head[x]; i; i = nxt[i])
    {
    
    
        int y = to[i];
        if (!dfn[y])
        {
    
    
            tarjan(y);
            low[x] = min(low[x], low[y]);
        }
        else if (in[y])
            low[x] = min(low[x], dfn[y]);
    }
    if (low[x] == dfn[x])
    {
    
    
        ++scc, nr = 0;
        int y;
        do
        {
    
    
            y = st[top--], in[y] = 0;
            rec[++nr] = y, sc[y] = scc;
        } while (y != x);
        for (int i = 1; i <= nr; ++i)
            vs[rec[i]] = 0;
        dfs(x);
    }
}

int main()
{
    
    
    scanf("%d%d", &N, &M);
    tot = tot_c = 1;
    for (int i = 1, t, a, b; i <= M; ++i)
    {
    
    
        scanf("%d%d%d", &t, &a, &b);
        if (t == 1)
            add(b, a, 0), add(a, b, 0);
        else if (t == 2)
            add(a, b, 1);
        else if (t == 3)
            add(b, a, 0);
        else if (t == 4)
            add(b, a, 1);
        else if (t == 5)
            add(a, b, 0);
    }
    for (int i = 1; i <= N; ++i)
        add(0, i, 1);
    fg = 1;
    for (int i = 0; i <= N; ++i)
        if (!dfn[i])
            tarjan(i);
    if (!fg)
    {
    
    
        puts("-1");
        return 0;
    }
    for (int i = 0; i <= N; ++i)
        for (int j = head[i]; j; j = nxt[j])
        {
    
    
            int x = sc[i], y = sc[to[j]], z = cost[j];
            if (x != y)
                add_c(x, y, z), ++deg[y];
        }
    queue<int> q;
    for (int i = 1; i <= scc; ++i)
        if (!deg[i])
            q.push(i);
    while (q.size())
    {
    
    
        int x = q.front();
        q.pop();
        for (int i = hc[x]; i; i = nc[i])
        {
    
    
            int y = tc[i], z = cc[i];
            dp[y] = max(dp[y], dp[x] + z);
            if (!--deg[y])
                q.push(y);
        }
    }
    ll res = 0;
    for (int i = 1; i <= N; ++i)
        res += dp[sc[i]];
    printf("%lld\n", res);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/neweryyy/article/details/115016075