[JLOI2015]管道鏈接

Description
小銘銘最近進入了某情報部門,該部門正在被如何創建安全的通道鏈接困擾。該部門有 n 個情報站,用 1 到 n 的整數編號。給出 m 對情報站 ui;vi 和費用 wi,表示情報站 ui 和 vi 之間能夠花費 wi 單位資源創建通道。若是一個情報站通過若干個創建好的通道能夠到達另一個情報站,那麼這兩個情報站就創建了通道鏈接。形式化地,若 ui 和 vi 創建了通道,那麼它們創建了通道鏈接;若 ui 和 vi 均與 ti 創建了通道鏈接,那麼 ui 和 vi 也創建了通道鏈接。如今在全部的情報站中,有 p 個重要情報站,其中每一個情報站有一個特定的頻道。小銘銘面臨的問題是,須要花費最少的資源,使得任意相同頻道的情報站之間都創建通道鏈接。ios

Input
第一行包含三個整數 n;m;p,表示情報站的數量,能夠創建的通道數量和重要情報站的數量。接下來 m 行,每行包含三個整數 ui;vi;wi,表示能夠創建的通道。最後有 p 行,每行包含
兩個整數 ci;di,表示重要情報站的頻道和情報站的編號。安全

Output
輸出一行一個整數,表示任意相同頻道的情報站之間都創建通道鏈接所花費的最少資源總量。ui

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 4spa

Sample Output
4code

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。ip


斯坦納樹+狀壓dpci

首先求出\(f_{sta}\)表示包含頻率集合\(sta\)的最小斯坦納生成森林的值,因而咱們有
\[f_{sta}=min\{f_s+f_{sta-s}|s\subset sta\}\]資源

而後,就瞎jb亂搞吧get

/*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;
}
相關文章
相關標籤/搜索