BZOJ2286 [Sdoi2011]消耗戰


Description

在一場戰爭中,戰場由n個島嶼和n-1個橋梁組成,保證 每兩個島嶼間有且僅有一條路徑可達。現在,我軍已經偵查到敵軍的總部在編號為1的島嶼,而且他們已經沒有足夠多的能源維系戰斗,我軍勝利在望。已知在其他 k個島嶼上有豐富能源,為了防止敵軍獲取能源,我軍的任務是炸毀一些橋梁,使得敵軍不能到達任何能源豐富的島嶼。由於不同橋梁的材質和結構不同,所以炸毀 不同的橋梁有不同的代價,我軍希望在滿足目標的同時使得總代價最小。
偵查部門還發現,敵軍有一台神秘機器。即使我軍切斷所有能源之后,他們也可以用那台機器。機器產生的效果不僅僅會修復所有我軍炸毀的橋梁,而且 會重新隨機資源分布(但可以保證的是,資源不會分布到1號島嶼上)。不過偵查部門還發現了這台機器只能夠使用m次,所以我們只需要把每次任務完成即可。

Input

第一行一個整數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,表示資源豐富島嶼的編號。

 

Output

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

 

 

Sample Input

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

Sample Output

12
32
22

HINT

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

 

正解:虛樹+樹形DP

解題報告:

  聽說這是虛樹裸題,於是我跑過來AC了。

  考慮直接樹形DP,點數過多,而且大多數沒有用,會超時。那我們想保留一些必要的點,而去掉無用的點。

  首先虛樹的構建也是一個很神的算法。網上代碼很多,但是總結還是少。

   遙遙的題解,傳送門:http://user.qzone.qq.com/872191552/blog/1464431226

  考慮維護一個棧,每次往里面插新結點,然后特判一下二者的lca和棧頂元素的情況。具體的看遙遙的吧,懶得說了,等切世界樹的時候再寫一份詳細的吧。

   

  UPD:

  昨天太懶了。現在還是仔細說一遍吧。

  具體做法挺簡單,就是要想清楚才行。首先把詢問點根據原樹DFS序排序,顯然這些點都要出現在虛樹中來,而且為了保證結構不被破壞,另外一些跟他們有關系的點都要加入到虛樹中來。我們用一個棧,維護原樹上的一條鏈,自棧底到棧頂,深度由小變大。每次考慮插入詢問點進棧。如果插入點的祖先是棧頂元素,那么直接插入即可,因為反正是一條鏈上的結點。如果不是的話,那么只有可能分居他們的lca的兩棵子樹中。現在我們就需要分類討論,如果棧頂元素的下一位的深度比lca深,那么我們需要不斷彈出棧頂元素,並且在彈出之前與棧頂下一位連一下邊。直到lca深度比棧頂元素深,此時把lca加入棧,把需要插入的點加入棧,繼續往下處理。又因為我們是按DFS序做的,這樣就可以保證我們開始說的,維護的是樹上的一條鏈。之后再DP就可以了。

 

  1 //It is made by jump~
  2 #include <iostream>
  3 #include <cstdlib>
  4 #include <cstring>
  5 #include <cstdio>
  6 #include <cmath>
  7 #include <algorithm>
  8 #include <ctime>
  9 #include <vector>
 10 #include <queue>
 11 #include <map>
 12 #include <set>
 13 #ifdef WIN32   
 14 #define OT "%I64d"
 15 #else
 16 #define OT "%lld"
 17 #endif
 18 using namespace std;
 19 typedef long long LL;
 20 const int MAXN = 250011;
 21 LL inf;//inf不能開小了
 22 int n,m,ecnt,tot,id[MAXN],k;
 23 int first[MAXN],next[MAXN*2],to[MAXN*2],w[MAXN*2];
 24 LL val[MAXN];//這個點到根上的最小邊權
 25 int jump[MAXN][19],deep[MAXN];
 26 int que[MAXN],top,Stack[MAXN];
 27 int head[MAXN];
 28 LL f[MAXN];
 29 
 30 inline int getint()
 31 {
 32        int w=0,q=0;
 33        char c=getchar();
 34        while((c<'0' || c>'9') && c!='-') c=getchar();
 35        if (c=='-')  q=1, c=getchar();
 36        while (c>='0' && c<='9') w=w*10+c-'0', c=getchar();
 37        return q ? -w : w;
 38 }
 39 
 40 struct edge{
 41     int to,next;
 42 }e[MAXN];
 43 
 44 inline LL min(LL x,LL y){ if(x<y) return x;  return y; }
 45 
 46 inline void dfs(int x,int fa){
 47     jump[x][0]=fa; id[x]=++ecnt;//作出dfs序
 48     for(int i=1;i<=18;i++) jump[x][i]=jump[jump[x][i-1]][i-1];
 49     for(int i=first[x];i;i=next[i]) {
 50     int v=to[i];
 51     if(v==fa) continue;
 52     val[v]=min(w[i],val[x]); deep[v]=deep[x]+1;
 53     dfs(v,x);
 54     }
 55 }
 56 
 57 inline bool cmp(int a,int b){return id[a]<id[b];}
 58 
 59 inline int lca(int x,int y){
 60     if(deep[x]<deep[y]) swap(x,y);
 61     int t=0; while((1<<t) <= deep[x]) t++;
 62     t--; for(int i=t;i>=0;i--) if(deep[x]-(1<<i)>=deep[y]) x=jump[x][i];
 63     if(x==y) return y;
 64     for(int i=t;i>=0;i--) if(jump[x][i]!=jump[y][i]) { x=jump[x][i]; y=jump[y][i]; } 
 65     return jump[x][0];
 66 }
 67 
 68 inline void link(int x,int y){ if(x==y) return ; e[++ecnt].next=head[x]; head[x]=ecnt; e[ecnt].to=y;}
 69 
 70 inline void dp(int x){
 71     LL lin=0; f[x]=val[x];
 72     for(int i=head[x];i;i=e[i].next) {
 73     dp(e[i].to);
 74     lin+=f[e[i].to];
 75     }
 76     head[x]=0;//退出的時候順便清空
 77     if(!lin) f[x]=val[x];
 78     else if(lin<f[x]) f[x]=lin;
 79 }
 80 
 81 inline void solve(){//斷絕到根結點1的路徑
 82     m=getint(); for(int i=1;i<=m;i++) que[i]=getint();
 83     sort(que+1,que+m+1,cmp);//按dfs序排序
 84     tot=0; que[++tot]=que[1]; 
 85     for(int i=2;i<=m;i++) if(lca(que[i],que[tot])!=que[tot]) que[++tot]=que[i];//應該是和tot比較
 86     //在下面的肯定不用計算,只要切斷上部的即可
 87     top=0;Stack[++top]=1; int grand;//最近公共祖先
 88     ecnt=0;
 89     for(int i=1;i<=tot;i++) {//分類討論
 90     grand=lca(Stack[top],que[i]);
 91     while(1) {
 92         if(deep[Stack[top-1]]<=deep[grand]) {//分別處在兩個子樹,grand深度更大!!!
 93         link(grand,Stack[top]); top--;
 94         if(Stack[top]!=grand) Stack[++top]=grand;
 95         break;
 96         }
 97         link(Stack[top-1],Stack[top]); top--;
 98     }
 99     if(Stack[top]!=que[i]) Stack[++top]=que[i];//在同一子樹
100     }
101     top--;
102     while(top) link(Stack[top],Stack[top+1]),top--;//剩余的記得連上
103     dp(1);
104     printf(OT"\n",f[1]);
105 }
106 
107 inline void work(){
108     n=getint(); int x,y,z;
109     inf=1;for(int i=1;i<=60;i++) inf*=2;
110     for(int i=1;i<n;i++) {
111     x=getint(); y=getint(); z=getint();
112     next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; w[ecnt]=z;
113     next[++ecnt]=first[y]; first[y]=ecnt; to[ecnt]=x; w[ecnt]=z;
114     }
115     val[1]=inf; ecnt=0; deep[1]=0; dfs(1,0);
116     k=getint(); for(int i=1;i<=k;i++) solve();
117 }
118 
119 int main()
120 {
121   work();
122   return 0;
123 }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM