[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

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。

斯坦纳树+状压dp

首先求出\(f_{sta}\)表示包含频率集合\(sta\)的最小斯坦纳生成森林的值,于是我们有
\[f_{sta}=min\{f_s+f_{sta-s}|s\subset sta\}\]

然后,就瞎jb乱搞吧

/*program from Wolfycz*/
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define inf 0x7f7f7f7f
using namespace std;
typedef long long ll;
typedef unsigned int ui;
typedef unsigned long long ull;
inline int read(){
    int x=0,f=1;char ch=getchar();
    for (;ch<'0'||ch>'9';ch=getchar())  if (ch=='-')    f=-1;
    for (;ch>='0'&&ch<='9';ch=getchar())    x=(x<<1)+(x<<3)+ch-'0';
    return x*f;
}
inline void print(int x){
    if (x>=10)     print(x/10);
    putchar(x%10+'0');
}
const int N=1e3,M=3e3,K=10;
int pre[(M<<1)+10],now[N+10],child[(M<<1)+10],val[(M<<1)+10],tot;
int f[N+10][(1<<K)+10],h[N+10],Ans[(1<<K)+10],sum[K+10],tmp[K+10];
bool vis[N+10];
struct S1{
    int col,ID;
    void join(){col=read(),ID=read();}
}imp[K+10];//important
int n,m,k;
void join(int x,int y,int z){pre[++tot]=now[x],now[x]=tot,child[tot]=y,val[tot]=z;}
bool check(int sta){
    memset(tmp,0,sizeof(tmp));
    for (int i=1;i<=k;i++)  if ((1<<(i-1))&sta) tmp[imp[i].col]++;
    for (int i=1;i<=K;i++)  if (tmp[i]&&tmp[i]!=sum[i]) return 0;
    return 1;
}
void spfa(int sta){
    int head=0,tail=0;
    for (int i=1;i<=n;i++)  h[++tail]=i,vis[i]=1;
    while (head!=tail){
        if (++head>N)   head=1;
        int Now=h[head];
        for (int p=now[Now],son=child[p];p;p=pre[p],son=child[p]){
            if (f[son][sta]>f[Now][sta]+val[p]){
                f[son][sta]=f[Now][sta]+val[p];
                if (!vis[son]){
                    if (++tail>N)   tail=1;
                    vis[h[tail]=son]=1;
                }
            }
        }
        vis[Now]=0;
    }
}
int main(){
    n=read(),m=read(),k=read();
    for (int i=1;i<=m;i++){
        int x=read(),y=read(),z=read();
        join(x,y,z),join(y,x,z);
    }
    memset(f,63,sizeof(f));
    memset(Ans,63,sizeof(Ans));
    for (int i=1;i<=k;i++)  imp[i].join(),sum[imp[i].col]++;
    for (int i=1;i<=k;i++)  f[imp[i].ID][1<<(i-1)]=0;
    for (int sta=0;sta<1<<k;sta++){
        for (int i=1;i<=n;i++)
            for (int s=(sta-1)&sta;s;s=(s-1)&sta)
                f[i][sta]=min(f[i][sta],f[i][s]+f[i][sta^s]);
        spfa(sta);
    }
    for (int sta=0;sta<1<<k;sta++)  for (int i=1;i<=n;i++)  Ans[sta]=min(Ans[sta],f[i][sta]);
    for (int sta=0;sta<1<<k;sta++){
        if (!check(sta))    continue;
        for (int s=(sta-1)&sta;s;s=(s-1)&sta){
            if (!check(s))  continue;
            Ans[sta]=min(Ans[sta],Ans[s]+Ans[sta^s]);
        }
    }
    printf("%d\n",Ans[(1<<k)-1]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Wolfycz/p/9697964.html