[SDOI2011]消耗戰
題目描述
在一場戰爭中,戰場由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
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]消耗戰