//题意:给定一些限制条件,询问满足条件的任一正数解是什么。(详细题意搜原题) //思路: 本题有几个额外信息很关键 // 最大人数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; }
标签:P3275,int,od,back,差分,low,push,SCOI2011,mp From: https://www.cnblogs.com/Aacaod/p/17051513.html