[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;
}
posted @ 2021-08-28 21:04  fhq_treap  阅读(50)  评论(0编辑  收藏  举报