[JLOI2015]管道连接
考虑到答案等价于求包含于所有关键集合的斯坦纳森林。
且强制让关键集合联通。
那我们只要枚举在森林中的一颗联通树,然后对他跑斯坦纳树即可,最后统计答案。
数据水的不行。看到题解区一堆代码错也能过的。
#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#define ll long long
#define N 11
#define M 1005
ll n,m,p;
ll f[M][1 << N],g[1 << N];
struct P{int to,next,val;}e[M * 10];
ll head[M],cnt;
inline void add(int x,int y,int v){
e[++cnt].to = y;
e[cnt].next = head[x];
head[x] = cnt;
e[cnt].val = v;
}
std::queue<int>QWQ;
bool vis[M];
inline void spfa(ll ID){
for(int i = 1;i <= n;++i)
if(f[i][ID] < 1e9)QWQ.push(i),vis[i] = 1;
while(!QWQ.empty()){
int u = QWQ.front();
QWQ.pop();
for(int i = head[u];i;i = e[i].next){
int v = e[i].to;
if(f[v][ID] > f[u][ID] + e[i].val){
f[v][ID] = f[u][ID] + e[i].val;
if(!vis[v]){
vis[v] = 1;
QWQ.push(v);
}
}
}
vis[u] = 0;
}
}
ll ID[M],num[M],w[1 << N];
int main(){
scanf("%lld%lld%lld",&n,&m,&p);
for(int i = 1;i <= m;++i){
ll x,y,v;
scanf("%lld%lld%lld",&x,&y,&v);
add(x,y,v);
add(y,x,v);
}
std::memset(ID,-1,sizeof(ID));
for(int i = 1;i <= p;++i){
ll x,y;
scanf("%lld%lld",&x,&y);
ID[y] = i - 1;
num[x] |= (1 << (i - 1));
}
std::memset(f,60,sizeof(f));
for(int i = 1;i <= n;++i)
if(ID[i] == -1)f[i][0] = 0;else f[i][(1 << ID[i])] = 0;
for(ll i = 0;i < (1ll << p);++i){
if(i){
for(int k = 1;k <= n;++k)
for(ll j = (i - 1) & i;j;j = (j - 1) & i)
f[k][i] = std::min(f[k][i],f[k][j] + f[k][i ^ j]);
}
spfa(i);
}
std::memset(w,60,sizeof(w));
for(ll i = 0;i < (1 << p);++i)
for(int j = 1;j <= n;++j)
w[i] = std::min(w[i],f[j][i]);
std::memset(g,60,sizeof(g));
g[0] = 0;
for(ll i = 1;i < (1 << p);++i)
for(ll j = i;j;j = (j - 1) & i){
ll s = 0;
for(ll k = 1;k <= p;++k)
if(j & num[k]) s |= num[k];
g[i] = std::min(g[i],g[i ^ j] + w[s]);
}
std::cout<<g[(1 << p) - 1]<<std::endl;
}