[BZOJ4006][JLOI2015]管道连接
[BZOJ4006][JLOI2015]管道连接
试题描述
小铭铭最近进入了某情报部门,该部门正在被如何建立安全的通道连接困扰。
该部门有 \(n\) 个情报站,用 \(1\) 到 \(n\) 的整数编号。给出 \(m\) 对情报站 \(u_i,v_i\) 和费用 \(w_i\),表示情报站 \(u_i\) 和 \(v_i\) 之间可以花费 \(w_i\) 单位资源建立通道。
如果一个情报站经过若干个建立好的通道可以到达另外一个情报站,那么这两个情报站就建立了通道连接。形式化地,若 \(u_i\) 和 \(v_i\) 建立了通道,那么它们建立了通道连接;若 \(u_i\) 和 \(v_i\) 均与 \(t_i\) 建立了通道连接,那么 \(u_i\) 和 \(v_i\) 也建立了通道连接。
现在在所有的情报站中,有 \(p\) 个重要情报站,其中每个情报站有一个特定的频道。小铭铭面临的问题是,需要花费最少的资源,使得任意相同频道的情报站之间都建立通道连接。
输入
第一行包含三个整数 \(n,m,p\),表示情报站的数量,可以建立的通道数量和重要情报站的数量。接下来 \(m\) 行,每行包含三个整数 \(u_i,v_i,w_i\),表示可以建立的通道。最后有 \(p\) 行,每行包含两个整数 \(c_i,d_i\),表示重要情报站的频道和情报站的编号。
输出
输出一行一个整数,表示任意相同频道的情报站之间都建立通道连接所花费的最少资源总量。
输入示例
5 8 4
1 2 3
1 3 2
1 5 1
2 4 2
2 5 1
3 4 3
3 5 1
4 5 1
1 1
1 2
2 3
2 4
输出示例
4
数据规模及约定
对于 \(100\%\) 的数据,\(0 < c_i \le p \le 10\); \(0 < u_i,v_i,d_i \le n \le 1000\); \(0 \le m \le 3000\); \(0 \le w_i \le 20000\)。
题解
斯坦纳树,后面再搞一次 dp,都比较简单。
斯坦纳树可以去看这篇。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <queue>
using namespace std;
#define rep(i, s, t) for(int i = (s); i <= (t); i++)
#define dwn(i, s, t) for(int i = (s); i >= (t); i--)
const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {
if(Head == Tail) {
int l = fread(buffer, 1, BufferSize, stdin);
Tail = (Head = buffer) + l;
}
return *Head++;
}
int read() {
int x = 0, f = 1; char c = Getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
return x * f;
}
#define maxn 1010
#define maxp 15
#define maxm 6010
#define maxs 1024
#define oo 2147483647
int n, m, p, head[maxn], nxt[maxm], to[maxm], dist[maxm], col[maxp];
void AddEdge(int a, int b, int c) {
to[++m] = b; dist[m] = c; nxt[m] = head[a]; head[a] = m;
swap(a, b);
to[++m] = b; dist[m] = c; nxt[m] = head[a]; head[a] = m;
return ;
}
#define D f[Set]
struct Node {
int u, d;
Node() {}
Node(int _, int __): u(_), d(__) {}
bool operator < (const Node& t) const { return d > t.d; }
};
priority_queue <Node> Q;
int f[maxs][maxn];
bool vis[maxn];
void ShortPath(int Set) {
memset(vis, 0, sizeof(vis));
rep(i, 1, n) if(D[i] < oo) Q.push(Node(i, D[i]));
while(!Q.empty()) {
int u = Q.top().u; Q.pop();
if(vis[u]) continue;
vis[u] = 1;
for(int e = head[u]; e; e = nxt[e]) if(D[to[e]] > D[u] + dist[e]) {
D[to[e]] = D[u] + dist[e];
if(!vis[to[e]]) Q.push(Node(to[e], D[to[e]]));
}
}
return ;
}
int F[maxs], ans[maxs];
int main() {
n = read(); int M = read(); p = read();
rep(i, 1, M) {
int a = read(), b = read(), c = read();
AddEdge(a, b, c);
}
int all = (1 << p) - 1;
rep(s, 0, all) rep(i, 1, n) f[s][i] = oo;
rep(i, 0, p - 1) {
col[i] = read();
f[1<<i][read()] = 0;
}
rep(s, 0, all) {
rep(i, 1, n)
for(int ts = (s - 1 & s); ts; ts = (ts - 1 & s))
f[s][i] = min(f[s][i], f[ts][i] + f[s^ts][i]);
ShortPath(s);
}
rep(s, 0, all) {
F[s] = oo;
rep(i, 1, n) F[s] = min(F[s], f[s][i]);
}
rep(s, 0, all) {
ans[s] = F[s];
for(int ts = (s - 1 & s); ts; ts = (ts - 1 & s)) {
int Col = 0, Cor = 0;
rep(i, 0, p - 1)
if(ts >> i & 1) Col |= (1 << col[i] - 1);
else Cor |= (1 << col[i] - 1);
if(Col & Cor) continue;
ans[s] = min(ans[s], F[ts] + F[s^ts]);
}
}
printf("%d\n", ans[all]);
return 0;
}