斯坦纳树复习,我暑假的时候好像写过[JLOI2015]管道连接来着。
设$f_{i, s}$表示以$i$为根,$k$个重要点的连通状态为$s$,($0$代表没有连进最小生成树里面去,$1$代表连进了最小生成树里面去)的最小代价,那么可以写出两种转移。
1、$f_{i, s} = min(f_{i, t} + f_{i, s ^ t})$ $t in s$。
2、$f_{i, s} = min(f_{j,s} + val(j, i))$ 存在一条边$(j, i)$的权值为$val(j, i)$。
第一种转移我们用枚举子集的技巧实现,第二种转移类似于最短路的三角形不等式,可以用$spfa$和$dijskra$转移。
时间复杂度似乎是$O(2^kmlogm + n3^k)$。
Code:
#include <cstdio> #include <cstring> #include <queue> #include <iostream> using namespace std; #define R register typedef long long ll; typedef pair <ll, int> pin; const int N = 1e5 + 5; const int M = 4e5 + 5; const int K = 6; const ll inf = 0x3f3f3f3f3f3f3f3f; int n, m, k, a[K], tot = 0, head[N]; ll f[N][1 << K]; bool vis[N]; priority_queue <pin> Q; struct Edge { int to, nxt, val; } e[M]; inline void add(int from, int to, int val) { e[++tot].to = to; e[tot].val = val; e[tot].nxt = head[from]; head[from] = tot; } /*template <typename T> inline void read(T &X) { X = 0; char ch = 0; T op = 1; for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') op = -1; for(; ch >= '0' && ch <= '9'; ch = getchar()) X = (X << 3) + (X << 1) + ch - 48; X *= op; } */ namespace IOread{ const int L = 1 << 15; char buffer[L], *S, *T; inline char Getchar() { if(S == T) { T = (S = buffer) + fread(buffer, 1, L, stdin); if(S == T) return EOF; } return *S++; } template <class T> inline void read(T &X) { char ch; T op = 1; for(ch = Getchar(); ch > '9' || ch < '0'; ch = Getchar()) if(ch == '-') op = -1; for(X = 0; ch >= '0' && ch <= '9'; ch = Getchar()) X = (X << 1) + (X << 3) + ch - '0'; X *= op; } } using namespace IOread; template <typename T> inline void chkMin(T &x, T y) { if(y < x) x = y; } inline void dij(int s) { memset(vis, 0, sizeof(vis)); for(; !Q.empty(); ) { int x = Q.top().second; Q.pop(); if(vis[x]) continue; vis[x] = 1; for(int i = head[x]; i; i = e[i].nxt) { int y = e[i].to; if(f[y][s] > f[x][s] + e[i].val) { f[y][s] = f[x][s] + e[i].val; Q.push(pin(-f[y][s], y)); } } } } int main() { read(n), read(k), read(m); memset(f, 0x3f, sizeof(f)); for(R int i = 1; i <= k; i++) { read(a[i]); f[a[i]][1 << (i - 1)] = 0LL; } for(R int i = 1; i <= m; i++) { int x, y, v; read(x), read(y), read(v); add(x, y, v), add(y, x, v); } for(R int s = 1; s < (1 << k); s++) { for(R int i = 1; i <= n; i++) { for(R int t = s & (s - 1); t; t = (t - 1) & s) chkMin(f[i][s], f[i][t] + f[i][s ^ t]); if(f[i][s] != inf) Q.push(pin(-f[i][s], i)); } dij(s); } ll ans = inf; for(R int i = 1; i <= n; i++) chkMin(ans, f[i][(1 << k) - 1]); printf("%lld ", ans); return 0; }
然而我在$loj$上卡了一面都没卡过去……大常数选手很受打击啊