1. 程式人生 > >[SDOI2011]消耗戰

[SDOI2011]消耗戰

發現 sig define 現在 內部 int 每次 bre 虛樹

題目描述

在一場戰爭中,戰場由n個島嶼和n-1個橋梁組成,保證每兩個島嶼間有且僅有一條路徑可達。現在,我軍已經偵查到敵軍的總部在編號為1的島嶼,而且他們已經沒有足夠多的能源維系戰鬥,我軍勝利在望。已知在其他k個島嶼上有豐富能源,為了防止敵軍獲取能源,我軍的任務是炸毀一些橋梁,使得敵軍不能到達任何能源豐富的島嶼。由於不同橋梁的材質和結構不同,所以炸毀不同的橋梁有不同的代價,我軍希望在滿足目標的同時使得總代價最小。

偵查部門還發現,敵軍有一臺神秘機器。即使我軍切斷所有能源之後,他們也可以用那臺機器。機器產生的效果不僅僅會修復所有我軍炸毀的橋梁,而且會重新隨機資源分布(但可以保證的是,資源不會分布到1號島嶼上)。不過偵查部門還發現了這臺機器只能夠使用m次,所以我們只需要把每次任務完成即可。

輸入輸出格式

輸入格式:

第一行一個整數n,代表島嶼數量。

接下來n-1行,每行三個整數u,v,w,代表u號島嶼和v號島嶼由一條代價為c的橋梁直接相連,保證1<=u,v<=n且1<=c<=100000。

第n+1行,一個整數m,代表敵方機器能使用的次數。

接下來m行,每行一個整數ki,代表第i次後,有ki個島嶼資源豐富,接下來k個整數h1,h2,…hk,表示資源豐富島嶼的編號。

輸出格式:

輸出有m行,分別代表每次任務的最小代價。

輸入輸出樣例

輸入樣例#1:
10
1 5 13
1 9 6
2 1 19
2 4 8
2 3 91
5 6 8
7 5 4
7 8 31
10 7 9
3
2 10 6
4 5 7 8 3
3 9 4 6
輸出樣例#1:
12
32
22

說明

【數據規模和約定】

對於10%的數據,2<=n<=10,1<=m<=5,1<=ki<=n-1

對於20%的數據,2<=n<=100,1<=m<=100,1<=ki<=min(10,n-1)

對於40%的數據,2<=n<=1000,m>=1,sigma(ki)<=500000,1<=ki<=min(15,n-1)

對於100%的數據,2<=n<=250000,m>=1,sigma(ki)<=500000,1<=ki<=n-1

題解:

  這個題目,首先我們考慮暴力dp,設dp[i]表示保證子樹內部都合法的最小花費,那麽顯然,dp[i]=sum(dp[to]),dp[i]=min(dp[i],val[i]),val[i]表示i到根節點路徑上的最小邊權。然而看看數據範圍……

  想怎麽優化,顯然每次詢問的正真用到的關鍵點其實是很少的,所以我們可以打一下虛樹,這樣就可以把詢問復雜度降為m*k*logk。於是就套一下虛樹模板就可以了(然而我是現學的)。

代碼:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#define MAXN 400100
#define ll long long
using namespace std;
ll f[MAXN],val[MAXN];
int top[MAXN],size[MAXN],dep[MAXN],son[MAXN],dfn[MAXN],fa[MAXN],b[MAXN];
int s[MAXN*2],q[MAXN];
struct edge{
    int first;
    int next;
    int to;
    int quan;
}a[MAXN*2],a2[MAXN*2];
int n,m,num=0,num2=0;

void addedge(int from,int to,int quan){
    a[++num].to=to;
    a[num].quan=quan;
    a[num].next=a[from].first;
    a[from].first=num;
}

void dfs1(int now,int F){
    fa[now]=F,size[now]=1,dep[now]=dep[F]+1;
    for(int i=a[now].first;i;i=a[i].next){
        int to=a[i].to,quan=a[i].quan;if(to==F) continue;
        val[to]=min((ll)quan,val[now]);
        dfs1(to,now);
        size[now]+=size[to];
        if(size[to]>size[son[now]]) son[now]=to;
    }
}

void dfs2(int now,int tp){
    top[now]=tp;dfn[now]=++num2;
    if(son[now]) dfs2(son[now],tp);
    for(int i=a[now].first;i;i=a[i].next){
        int to=a[i].to;
        if(to==fa[now]||to==son[now]) continue;
        dfs2(to,to);
    }
}

int Lca(int x,int y){
    int topx=top[x],topy=top[y];
    while(topx!=topy){
        if(dep[topx]<dep[topy]) swap(topx,topy),swap(x,y);
        x=fa[topx];
        topx=top[x];
    }
    if(dep[x]<dep[y]) return x;
    return y;
}

bool cmp(int x,int y){
    return dfn[x]<dfn[y];
}

void addedge2(int from,int to,int quan){
    a2[++num].to=to;
    a2[num].quan=quan;
    a2[num].next=a2[from].first;
    a2[from].first=num;
}

void DP(int now,int FA){
    if(b[now]) f[now]=val[now];
    else f[now]=0;
    for(int i=a2[now].first;i;i=a2[i].next){
        int to=a2[i].to;
        if(to==FA||to==now) continue;
        DP(to,now);
        f[now]+=f[to];
    }
    f[now]=min(f[now],val[now]);
    b[now]=a2[now].first=0;
}

void work(){
    int tot,tp=0;
    scanf("%d",&tot);
    for(int i=1;i<=tot;i++) scanf("%d",&q[i]),b[q[i]]=1;
    sort(q+1,q+tot+1,cmp);
    num=0;
    s[++tp]=1;
    for(int i=1;i<=tot;i++){
        int lca=Lca(q[i],s[tp]);
        while(1){
            if(dep[s[tp-1]]<=dep[lca]){
                addedge2(s[tp],lca,0),addedge2(lca,s[tp],0);tp--;
                if(s[tp]!=lca) s[++tp]=lca;
                break;
            }
            addedge2(s[tp],s[tp-1],0),addedge2(s[tp-1],s[tp],0);tp--;
        }
        s[++tp]=q[i];
    }
    while(tp>1) {addedge2(s[tp],s[tp-1],0),addedge2(s[tp-1],s[tp],0);tp--;}
    DP(1,0);
    printf("%lld\n",f[1]);
}

int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n-1;i++){
        int x,y,z;scanf("%d%d%d",&x,&y,&z);
        addedge(x,y,z),addedge(y,x,z);
    }
    val[1]=1ll<<60;
    dfs1(1,0);dfs2(1,1);
    scanf("%d",&m);
    while(m--) work();
    return 0;
}

[SDOI2011]消耗戰