bzoj4006 [JLOI2015]管道连接
Description
小铭铭最近进入了某情报部门,该部门正在被如何建立安全的通道连接困扰。
该部门有 n 个情报站,用 1 到 n 的整数编号。给出 m 对情报站 ui;vi 和费用 wi,表示情报站 ui 和 vi 之间可以花费 wi 单位资源建立通道。
如果一个情报站经过若干个建立好的通道可以到达另外一个情报站,那么这两个情报站就建立了通道连接。形式化地,若 ui 和 vi 建立了通道,那么它们建立了通道连接;若 ui 和 vi 均与 ti 建立了通道连接,那么 ui 和 vi 也建立了通道连接。
现在在所有的情报站中,有 p 个重要情报站,其中每个情报站有一个特定的频道。小铭铭面临的问题是,需要花费最少的资源,使得任意相同频道的情报站之间都建立通道连接。
Input
第一行包含三个整数 n;m;p,表示情报站的数量,可以建立的通道数量和重要情报站的数量。
接下来 m 行,每行包含三个整数 ui;vi;wi,表示可以建立的通道。最后有 p 行,每行包含两个整数 ci;di,表示重要情报站的频道和情报站的编号。
Output
输出一行一个整数,表示任意相同频道的情报站之间都建立通道连接所花费的最少资源总量。
Sample Input
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
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
Sample Output
4
HINT
选择 (1; 5); (3; 5); (2; 5); (4; 5) 这 4 对情报站连接。
对于 100% 的数据,0 <ci <= p <= 10; 0 <ui;vi;di <= n <= 1000; 0 <= m <= 3000; 0 <= wi <=20000。
正解:斯坦纳森林。
斯坦纳树加强版——斯坦纳森林。
首先,我们直接把所有关键点状压起来,整个跑一遍斯坦纳树。
然后对于每个状态,我们忽略它的根是什么,直接取每层状态的最小值。
然后我们就可以直接枚举子集合并了,仔细想想发现这样做是对的。
但是我们要注意一点,两个不同根的状态合并,合并以后可能是不连通的,所以我们要判断状态的合法性。
如果一个状态既不是不包含一个频道,又不是全部包含一个频道,那么这个状态是不合法的,可以直接丢掉。
1 //It is made by wfj_2048~ 2 #include <algorithm> 3 #include <iostream> 4 #include <complex> 5 #include <cstring> 6 #include <cstdlib> 7 #include <cstdio> 8 #include <vector> 9 #include <cmath> 10 #include <queue> 11 #include <stack> 12 #include <map> 13 #include <set> 14 #define inf (1<<30) 15 #define all (1<<p) 16 #define N (1010) 17 #define il inline 18 #define RG register 19 #define ll long long 20 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout) 21 22 using namespace std; 23 24 struct edge{ int nt,to,dis; }g[10010]; 25 26 int f[N][1<<11],ff[1<<11],head[N],vis[N],q[1000010],mt[N],cal[12],id[12][12],n,m,p,num; 27 28 il int gi(){ 29 RG int x=0,q=1; RG char ch=getchar(); 30 while ((ch<'0' || ch>'9') && ch!='-') ch=getchar(); 31 if (ch=='-') q=-1,ch=getchar(); 32 while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); 33 return q*x; 34 } 35 36 il void insert(RG int from,RG int to,RG int dis){ 37 g[++num]=(edge){head[from],to,dis},head[from]=num; return; 38 } 39 40 il int check(RG int s){ 41 for (RG int i=1;i<=10;++i) 42 if ((s&cal[i]) && (s&cal[i])!=cal[i]) return 0; 43 return 1; 44 } 45 46 il void spfa(RG int s){ 47 RG int h=0,t=0; 48 for (RG int i=1;i<=n;++i) 49 if (f[i][s]!=f[0][0]) q[++t]=i,vis[i]=1; 50 while (h<t){ 51 RG int x=q[++h],v; 52 for (RG int i=head[x];i;i=g[i].nt){ 53 v=g[i].to; 54 if (f[v][s]>f[x][s]+g[i].dis){ 55 f[v][s]=f[x][s]+g[i].dis; 56 if (!vis[v]) vis[v]=1,q[++t]=v; 57 } 58 } 59 vis[x]=0; 60 } 61 return; 62 } 63 64 il void work(){ 65 n=gi(),m=gi(),p=gi(); 66 for (RG int i=1,u,v,w;i<=m;++i) 67 u=gi(),v=gi(),w=gi(),insert(u,v,w),insert(v,u,w); 68 memset(f,0x3f3f3f,sizeof(f)),memset(ff,0x3f3f3f,sizeof(ff)); 69 for (RG int i=1,x,y;i<=p;++i){ 70 x=gi(),y=gi(),id[x][++id[x][0]]=y; 71 mt[y]=1<<(i-1),f[y][mt[y]]=0; 72 } 73 for (RG int s=1;s<all;++s){ 74 for (RG int i=1;i<=n;++i){ 75 for (RG int sub=(s-1)&s;sub;sub=(sub-1)&s) 76 f[i][s]=min(f[i][s],f[i][sub]+f[i][s^sub]); 77 ff[s]=min(ff[s],f[i][s]); 78 } 79 spfa(s); 80 } 81 for (RG int i=1;i<=10;++i) 82 for (RG int j=1;j<=id[i][0];++j) cal[i]|=mt[id[i][j]]; 83 for (RG int s=1;s<all;++s){ 84 if (!check(s)) continue; 85 for (RG int sub=(s-1)&s;sub;sub=(sub-1)&s){ 86 if (!check(sub)) continue; 87 ff[s]=min(ff[s],ff[sub]+ff[s^sub]); 88 } 89 } 90 printf("%d\n",ff[all-1]); return; 91 } 92 93 int main(){ 94 File("pipe"); 95 work(); 96 return 0; 97 }