P3275 [SCOI2011]糖果 差分约束+最短路

//题意:给定一些限制条件,询问满足条件的任一正数解是什么。(详细题意搜原题)
//思路: 本题有几个额外信息很关键
//      最大人数1e5,连出去的边只有0和-1
//      如果我们直接冲差分约束,那么复杂度会冲到1e9之上,是跑不完所有数据的
//      所以我们为了减少时间复杂度,应当减少点数与边数,所以我们想到缩点
//      但是这题需判断是否无解(有负环),所以我们应当首先判断负环,不过好在这题的边权除了-1就只有0,所以我们缩点后对每个强联通分量进行判断,看有没有-1就行
//      另外当最短路图中没有环时,我们可以先拓扑排序然后按拓扑序求最短路
//      最后这题的复杂度应该接近O(n+m)
//
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 10;
int n, k;
vector<pair<int, int>> mp[N];
vector<pair<int, int>> nmp[2 * N];//重边对最短路没有影响
void read() {
    int od, a, b; cin >> od >> a >> b;
    if (od == 1) {
        mp[a].push_back({ b,0 });
        mp[b].push_back({ a,0 });
    }
    else if (od == 2) mp[b].push_back({ a,-1 });
    else if (od == 3) mp[a].push_back({ b,0 });
    else if (od == 4) mp[a].push_back({ b,-1 });
    else if (od == 5) mp[b].push_back({ a,0 });
}
int dfn[N], idx, bel[N], low[N], ins[N], cnt, sz[N];
stack<int> stk;
vector<int> scc[N]; 
void dfs(int x) {
    dfn[x] = low[x] = ++idx;
    stk.push(x);
    ins[x] = 1;
    for (auto v : mp[x]) {
        int tp = v.first;
        if (!dfn[tp]) {
            dfs(tp);
            low[x] = min(low[x], low[tp]);
        }
        else if (ins[tp]) low[x] = min(low[x], dfn[tp]);
    }
    if (dfn[x] == low[x]) {
        ++cnt;
        while (true) {
            int v = stk.top();
            scc[cnt].push_back(v);
            sz[cnt]++;
            ins[v] = false;
            bel[v] = cnt;
            stk.pop();
            if (v == x) break;
        }
    }
}
int rd[2 * N], cnt1, odr[N];
queue<int> lis;
void topo() {
    while (!lis.empty()) {
        int x = lis.front();
        odr[++cnt1] = x;
        for (auto y : nmp[x]) {
            rd[y.first]--;
            if (!rd[y.first]) lis.push(y.first);
        }
        lis.pop();
    }
}
int dis[2 * N];
signed main() {
    cin >> n >> k;
    for (int i = 1; i <= k; i++) read();
    for (int i = 1; i <= n; i++) mp[i].push_back({ 0,-1 });//最小糖果树是正数,所以满足xi-x0>=1
    for (int i = 1; i <= n; i++)
        if (!dfn[i]) dfs(i);

    for (int i = 1; i <= cnt; i++)
        for (auto x : scc[i]) 
            for (auto v : mp[x]) 
                if (bel[v.first] == i && v.second == -1) {
                    cout << -1; return 0;
                }
    //有负环
    for (int i = 1; i <= cnt; i++)
        for (auto x : scc[i])
            for (auto v : mp[x])
                if (bel[x] != bel[v.first]) {
                    nmp[n + i].push_back({ n + bel[v.first], v.second});//建新图
                    rd[n + bel[v.first]]++;
                }

    
    for (int i = 1 + n; i <= n + cnt; i++) dis[i] = 1 << 29;

    for (int i = 1 + n; i <= n + cnt; i++) {
        if (rd[i] == 0) {
            dis[i] = 0;
            lis.push(i);
        }
    }
    topo();

    for (int i = 1; i <= cnt1; i++)
        for (auto x : nmp[odr[i]])
            if (dis[odr[i]] + x.second < dis[x.first])
                dis[x.first] = dis[odr[i]] + x.second;

    int ans = 0, dis0 = dis[bel[0] + n];
    
    
    for (int i = 1 + n; i <= n + cnt; i++) {
        ans +=  sz[i - n] * (dis[i] - dis0);
    }

cout << ans;

return 0;
}

 

posted @ 2023-01-14 11:43  Aacaod  阅读(30)  评论(0编辑  收藏  举报