題意:
給出一顆n個節點有邊權的樹 和m個軍隊所在的位置 軍隊從某節點移動到相鄰節點要花費邊長度的時間 求最少要多少時間使得根節點(編號為1)到每個葉子的路徑上最少有一支軍隊(根節點不能有軍隊)
題解:
我們可以二分答案 那么問題就轉換為 在t的時間內軍隊能否控所有點
顯然一支軍隊在到根節點之前 如果能繼續向上走 那么這支軍隊能控制的點就會更多
維護bo[i]表示 從該點到所有該點子樹的葉子節點路徑上是否都有軍隊
對於每個不能走到根節點的軍隊 就讓他盡量向上走 直到不能走為止 將該點的bo值賦為1 然后可以用拓撲算出bo數組
這樣 根節點的所有兒子中bo為1的點就不用管 而剩下的點就需要派能走到根節點的軍隊到這些點控制
於是問題轉換為 有n0個點需要被控制 且控制該點需要Li的時間 還有m0支軍隊 每支軍隊有ti的時間 求這m0支軍隊是否能控制這n0個點
這原本這樣將L和t分別排序 貪心用兩個指針一個個掃就行了
但是要注意的是如果軍隊j就是從i點走到首都的 那么該軍隊不論有多少剩余時間都能控制該點(去年noip考試的時候好像就是沒想到這點)
這個問題我們只要做一個小轉換就行了 如果i點有軍隊從該點到達首都 那么用minarm[i]記下這些軍隊中剩余時間最少的是誰 當指針掃到i時先判斷minarm[i]是否被用過了 如果沒有 那么用minarm[i]來控制i 否則再在j指針上找軍隊
證明:如果minarm[i]沒被使用 那么t[j]>=t[minarm[i]] 所以在i點如果用minarm[i]則能省下一個剩余時間更多的軍隊 對於之后可能用到minarm[i]的點完全可以用j點代替
代碼:

1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 using std::sort; 5 const int N=50001; 6 struct inli{ 7 int next,data,lon; 8 inli(const int a=0,const int b=0,const int c=0): 9 next(a),data(b),lon(c){} 10 }line[N*2]; 11 struct info{ 12 int t,s; 13 info(const int a=0,const int b=0): 14 t(a),s(b){} 15 }ar[N],ci[N]; 16 inline bool cmp(info a,info b){ return a.s>b.s; } 17 int n,m,na,nc,fat[N],ti[N],gra[N],dis[N],arm[N],nl,son[N],mint[N],add,boo[N]; 18 int max(int x,int y){ return x>y ? x : y; } 19 void dfs(int t){ 20 int res=-1,bo1=0,bo2=0; 21 for (int i=son[t];i;i=line[i].next) 22 if (line[i].data!=fat[t]){ 23 int ne=line[i].data; 24 dfs(ne); 25 if (ti[ne]==-1) bo2=1; 26 if (ti[ne]-line[i].lon>=0) bo1=1; 27 res=max(res,ti[ne]-line[i].lon); 28 } 29 if (t!=1 && line[son[t]].next){ 30 if (bo1) ti[t]=max(ti[t],res); 31 else if (bo2) ti[t]=max(ti[t],-1); 32 else ti[t]=max(ti[t],0); 33 } 34 } 35 void maketi(int t){ 36 for (int i=1;i<=m;i++) 37 if (dis[arm[i]]>=t) ti[arm[i]]=t; 38 dfs(1); 39 } 40 void makear(int t){ 41 na=0; 42 for (int i=1;i<=m;i++) 43 if (dis[arm[i]]<t) ar[++na]=info(gra[arm[i]],t-dis[arm[i]]); 44 sort(ar+1,ar+na+1,cmp); 45 for (int i=1;i<=na;i++){ 46 int x=ar[i].t; 47 if (mint[x]==0) mint[x]=i; 48 else if (ar[mint[x]].s>ar[i].s) mint[x]=i; 49 } 50 } 51 void makeci(){ 52 nc=0; 53 for (int i=son[1];i;i=line[i].next) 54 if (ti[line[i].data]==-1) ci[++nc]=info(line[i].data,line[i].lon); 55 sort(ci+1,ci+nc+1,cmp); 56 } 57 bool work(){ 58 if (na<nc) return 0; 59 memset(boo,0,sizeof(boo)); 60 for (int i=1,j=1;i<=nc;i++) 61 if (!boo[mint[ci[i].t]] && mint[ci[i].t]) boo[mint[ci[i].t]]=1; 62 else{ 63 while (boo[j] && j<=na) ++j; 64 if (j>na) return 0; 65 if (ar[j].s<ci[i].s) return 0; 66 boo[j++]=1; 67 } 68 return 1; 69 } 70 bool check(int t){ 71 memset(ti,-1,sizeof(ti)); 72 memset(mint,0,sizeof(mint)); 73 maketi(t); 74 makear(t); 75 makeci(); 76 return work(); 77 } 78 int getans(){ 79 int l=-1,r=1000000000,mid; 80 while (l+1<r){ 81 mid=(l+r)/2; 82 if (check(mid)) r=mid; 83 else l=mid; 84 } 85 return r; 86 } 87 void makefat(int t){ 88 for (int i=son[t];i;i=line[i].next) 89 if (line[i].data!=fat[t]){ 90 int ne=line[i].data; 91 if (t==1) gra[ne]=ne; 92 else gra[ne]=gra[t]; 93 fat[ne]=t; 94 dis[ne]=dis[t]+line[i].lon; 95 makefat(ne); 96 } 97 } 98 int main(){ 99 freopen("blockade.in","r",stdin); 100 freopen("blockade.out","w",stdout); 101 scanf("%d",&n); 102 for (int x,y,z,i=1;i<n;i++){ 103 scanf("%d%d%d",&x,&y,&z); 104 line[++nl]=inli(son[x],y,z),son[x]=nl; 105 line[++nl]=inli(son[y],x,z),son[y]=nl; 106 if (x==1 || y==1) ++add; 107 } 108 makefat(1); 109 scanf("%d",&m); 110 if (m<add){ 111 printf("-1"); 112 return 0; 113 } 114 for (int i=1;i<=m;i++) scanf("%d",&arm[i]); 115 sort(arm+1,arm+m+1); 116 printf("%d",getans()); 117 fclose(stdin); 118 fclose(stdout); 119 }