CF 1430G Yet Another DAG Problem
阿新 • • 發佈:2021-01-20
- 給定一個有向無環圖,設定每個點的點權,使得每條邊的起點權值大於終點,最小化每條邊的邊權與起點與終點之差的乘積之和。
第一種方法可以將貢獻記在點上,對答案的貢獻為 \(a_i*( \sum w_{出邊} - \sum w_{入邊} )\)。於是可以從低到到高來安排每個點的權值。
設 \(f_{i,S}\) 為考慮到第 \(i\) 層,已安排了的集合為 \(S\),所需要的最小代價,每個點要被選入必須要所有比它小的選了之後才能選,列舉子集轉移即可。
第二種方法是將貢獻按相隔層數拆分,設 \(f_{S}\) 為已經權值確定的點的集合為 \(S\),最小所需要的代價和,轉移到下一個狀態 \(S'\)
#include <bits/stdc++.h> using namespace std; #define rep(i, a, b) for (int i = (a); i <= (b); i++) #define per(i, a, b) for (int i = (a); i >= (b); i--) #define fi first #define se second typedef long long LL; typedef pair <int, int> P; const int inf = 0x3f3f3f3f, mod = 1e9 + 7, N = 2e6 + 10; template <typename T> inline void rd_(T &x) { x = 0; int f = 1; char ch = getchar(); while (ch > '9' || ch < '0') { if (ch == '-') f = -1; ch = getchar(); } while (ch >= '0' && ch <= '9') x = x*10 + ch - '0', ch = getchar(); x *= f; } int n, m, a[N], b[N], c[N], deg[N], p[N], S, s[N], f[N], h[N], w[N], tot, ans[N]; vector <int> g[N]; int main() { //freopen("data.in", "r", stdin); rd_(n), rd_(m), S = (1<<n) - 1; rep (i, 1, m) { rd_(a[i]), rd_(b[i]), rd_(c[i]); deg[b[i]]++, g[a[i]].push_back(b[i]); } queue <int> q; rep (i, 1, n) if (!deg[i]) q.push(i); while (!q.empty()) { int u = q.front(); q.pop(); for (int v, i = 0; i < (int) g[u].size(); i++) { p[v = g[u][i]] |= (p[u] | (1<<u - 1)); if (!--deg[v]) q.push(v); } } rep (i, 0, S) { rep (j, 0, n - 1) if (i&(1<<j)) s[i] |= p[j + 1]; rep (j, 1, m) if ((i&(1<<a[j] - 1)) && ((i&(1<<b[j] - 1)) == 0)) w[i] += c[j]; // cout<<i<<" "<<w[i]<<endl; } memset(f, 0x3f, sizeof(f)); f[0] = 0; rep (i, 1, S) { for (int j = i; j; j = (j - 1)&i) { int k = i^j; if ((k&s[j]) == s[j] && f[i] > f[k] + w[k]) f[i] = f[k] + w[k], h[i] = j; } } while (S) { ++tot; rep (i, 0, n - 1) if (h[S]&(1<<i)) ans[i + 1] = tot; S -= h[S]; } rep (i, 1, n) printf("%d ", ans[i]); }