bzoj 4006: [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。
题解:
斯坦纳树变式,只需正常跑一边斯坦纳然后再加特判,这题中F[i][k]中第二维k不再表示k这个状态联通
而表示k状态中,相同频道的站都联通,所以我们最后再合并,避免漏解情况
可以理解为:斯坦纳跑出来的k是两个块用一些边连起来以后的联通块,而此题中一些边可以去掉,只需满足相同频道联通即可
1 #include <algorithm> 2 #include <iostream> 3 #include <cstdlib> 4 #include <cstring> 5 #include <cstdio> 6 #include <cmath> 7 using namespace std; 8 const int N=1005,M=3005; 9 int gi(){ 10 int str=0;char ch=getchar(); 11 while(ch>'9' || ch<'0')ch=getchar(); 12 while(ch>='0' && ch<='9')str=(str<<1)+(str<<3)+ch-48,ch=getchar(); 13 return str; 14 } 15 int head[N],num=0; 16 struct Lin{ 17 int next,to,dis; 18 }a[M<<1]; 19 void init(int x,int y,int z){ 20 a[++num].next=head[x];a[num].to=y;a[num].dis=z;head[x]=num; 21 } 22 int n,m,col,f[N][1<<10],P=0,tot,INF; 23 bool vis[N];int q[N*10],mod=N*10; 24 void spfa(int k){ 25 int t=0,sum=0,x,u; 26 for(int i=1;i<=n;i++) 27 if(f[i][k]!=INF)q[++sum]=i,vis[i]=true; 28 while(t!=sum){ 29 t++;if(t>=mod)t-=mod;x=q[t]; 30 for(int i=head[x];i;i=a[i].next){ 31 u=a[i].to; 32 if(f[x][k]+a[i].dis<f[u][k]){ 33 f[u][k]=f[x][k]+a[i].dis; 34 if(!vis[u]){ 35 vis[u]=true; 36 sum++;if(sum>=mod)sum-=mod;q[sum]=u; 37 } 38 } 39 } 40 vis[x]=false; 41 } 42 } 43 int ans[1<<10];int c[15][15],ts[1<<10]; 44 bool check(int states){ 45 for(int i=1;i<=10;i++){ 46 if(!ts[i])continue; 47 if((ts[i]&states) && ((states&ts[i])!=ts[i]))return false; 48 } 49 return true; 50 } 51 void work(){ 52 int x,y,z; 53 memset(f,127/3,sizeof(f));INF=f[0][0]; 54 n=gi();m=gi();col=gi(); 55 for(int i=1;i<=m;i++){ 56 x=gi();y=gi();z=gi(); 57 init(x,y,z);init(y,x,z); 58 } 59 for(int i=1;i<=col;i++){ 60 x=gi();y=gi(); 61 f[y][1<<(P++)]=0; 62 c[x][++c[x][0]]=P-1; 63 } 64 for(int i=1;i<=10;i++) 65 for(int j=1,tmp=c[i][0];j<=tmp;j++) 66 ts[i]|=(1<<c[i][j]); 67 tot=(1<<P)-1; 68 for(int s=1;s<=tot;s++){ 69 for(int i=1;i<=n;i++) 70 for(int j=(s-1)&s;j;j=(j-1)&s){ 71 if(f[i][j]+f[i][s-j]<f[i][s]){ 72 f[i][s]=f[i][j]+f[i][s-j]; 73 } 74 } 75 spfa(s); 76 } 77 memset(ans,127/3,sizeof(ans)); 78 for(int s=1;s<=tot;s++) 79 for(int i=1;i<=n;i++) 80 if(f[i][s]<ans[s])ans[s]=f[i][s]; 81 for(int s=1;s<=tot;s++){ 82 if(!check(s)){ 83 ans[s]=INF; 84 continue; 85 } 86 for(int j=(s-1)&s;j;j=(j-1)&s){ 87 if(!check(j))continue; 88 ans[s]=min(ans[s],ans[j]+ans[s-j]); 89 } 90 } 91 printf("%d\n",ans[tot]); 92 } 93 int main() 94 { 95 freopen("pp.in","r",stdin); 96 work(); 97 return 0; 98 }