1. 程式人生 > >洛谷 P2680 運輸計劃-二分+樹上差分(邊權覆蓋)

洛谷 P2680 運輸計劃-二分+樹上差分(邊權覆蓋)

有一點 會同 bin 相關 ont ima 飛船 adg 數量

P2680 運輸計劃

題目背景

公元 20442044 年,人類進入了宇宙紀元。

題目描述

公元20442044 年,人類進入了宇宙紀元。

L 國有 nn 個星球,還有 n-1n1 條雙向航道,每條航道建立在兩個星球之間,這 n-1n1 條航道連通了 LL 國的所有星球。

小 P 掌管一家物流公司, 該公司有很多個運輸計劃,每個運輸計劃形如:有一艘物流飛船需要從 u_iui? 號星球沿最快的宇航路徑飛行到 v_ivi? 號星球去。顯然,飛船駛過一條航道是需要時間的,對於航道 jj,任意飛船駛過它所花費的時間為 t_jtj?,並且任意兩艘飛船之間不會產生任何幹擾。

為了鼓勵科技創新, LL 國國王同意小 PP 的物流公司參與 LL 國的航道建設,即允許小PP 把某一條航道改造成蟲洞,飛船駛過蟲洞不消耗時間。

在蟲洞的建設完成前小 P 的物流公司就預接了 mm 個運輸計劃。在蟲洞建設完成後,這 mm 個運輸計劃會同時開始,所有飛船一起出發。當這 mm 個運輸計劃都完成時,小 PP 的物流公司的階段性工作就完成了。

如果小 PP 可以自由選擇將哪一條航道改造成蟲洞, 試求出小 PP 的物流公司完成階段性工作所需要的最短時間是多少?

輸入輸出格式

輸入格式:

第一行包括兩個正整數 n, mn,m,表示 L 國中星球的數量及小 P 公司預接的運輸計劃的數量,星球從 11 到 nn 編號。

接下來 n-1n1 行描述航道的建設情況,其中第 ii 行包含三個整數 a_i, b_iai?,bi?t_iti?,表示第 ii 條雙向航道修建在 a_iai?b_ibi? 兩個星球之間,任意飛船駛過它所花費的時間為 t_iti?。數據保證 1 \leq a_i,b_i \leq n1ai?,bi?n 且 0 \leq t_i \leq 10000ti?1000。

接下來 mm 行描述運輸計劃的情況,其中第 jj 行包含兩個正整數 u_juj?v_jvj?,表示第 jj 個運輸計劃是從 u_juj? 號星球飛往 v_jvj?號星球。數據保證 1 \leq u_i,v_i \leq n1ui?,vi?n

輸出格式:

一個整數,表示小 PP 的物流公司完成階段性工作所需要的最短時間。

輸入輸出樣例

輸入樣例#1: 復制
6 3 
1 2 3 
1 6 4 
3 1 7 
4 3 6 
3 5 5 
3 6 
2 5 
4 5
輸出樣例#1: 復制
11

說明

所有測試數據的範圍和特點如下表所示

技術分享圖片

請註意常數因子帶來的程序效率上的影響。

這道題有點東西。本來是想練手樹上差分的,發現很多題解都是用二分+樹鏈剖分寫的,看了一下,樹鏈剖分、線段樹、樹狀數組(求線段標記次數)、LCA(有的是用tarjan求的路徑的長度)。

一開始我看到這道題的時候,看到所有的計劃都一起跑,就有點傻了,一起跑可怎麽找,感覺直接暴力找回超時,後來算了一下,好像可以過。

我的思路就是:一開始的時候,先把所有的路徑的長度出來,然後通過dfs把lca的相關東西處理出來,每一條線段的長度先保存一下,然後二分,最長的路徑長度為右端點r,二分,每二分一次就遍歷一下所有的路徑,大於mid長度的,就樹上差分保存一下,然後dfs一下就把所有的線段的標記次數找出來了,然後找一下是不是有某條線段被標記的次數為加入的路徑的數量,如果有,就說明刪掉這條邊是可能成立的,最長的路徑-這條線段與mid比較一下就可以了,有一點就是標記的次數相同的找最長的那條線段,一開始智障,找到了就跳出去了,wa了。。。

還有一點就是存邊的時候數組開二倍,還有一點就是每一條線段的長度保存的時候,不能直接在輸入的時候保存,這樣會wa,emnnnn,有點不太懂為什麽。。。

測評姬有點情緒不穩,相同的代碼我第一次交有一個樣例點tle,再交一次一模一樣的,過了。。。試了好幾次,網卡的時候就容易tle。。。

代碼:

  1 //洛谷 P2680 運輸計劃 二分+樹上差分(邊覆蓋)
  2 #include<iostream>
  3 #include<cstring>
  4 #include<cstdio>
  5 #include<cmath>
  6 #include<algorithm>
  7 #include<cstdlib>
  8 using namespace std;
  9 typedef long long ll;
 10 const int maxn=3e5+10;
 11 
 12 struct node{
 13     int to,val,next;
 14 }edge[maxn<<1];
 15 
 16 int p1[maxn],p2[maxn],vv[maxn],maxx=0,ret,num,n,m,cnt,ans;
 17 int head[maxn<<1],sum[maxn],dep[maxn],fa[maxn][30],dis[maxn],len[maxn];
 18 
 19 void add(int x,int y,int v){edge[++cnt].to=y,edge[cnt].val=v,edge[cnt].next=head[x],head[x]=cnt;}
 20 
 21 void dfs(int u,int fath)
 22 {
 23     dep[u]=dep[fath]+1,fa[u][0]=fath;
 24     for(int i=0;fa[u][i];++i) fa[u][i+1]=fa[fa[u][i]][i];
 25     for(int i=head[u];i;i=edge[i].next){
 26         int v=edge[i].to,w=edge[i].val;
 27         if(v!=fath) dis[v]=dis[u]+w,vv[v]=w,dfs(v,u);//vv的dfs一開始寫挫了,直接寫到輸入那裏了
 28     }
 29 }
 30 
 31 int LCA(int u,int v)
 32 {
 33     if(dep[u]>dep[v]) swap(u,v);
 34     for(int i=21;i>=0;i--) if(dep[u]<=dep[v]-(1<<i)) v=fa[v][i];
 35     if(u==v) return u;
 36     for(int i=21;i>=0;i--) if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
 37     return fa[u][0];
 38 }
 39 
 40 void Dfs(int u,int fath)//標記之後的Dfs
 41 {
 42     for(int i=head[u];i;i=edge[i].next){
 43         int v=edge[i].to;
 44         if(v==fath) continue;
 45         Dfs(v,u);
 46         sum[u]+=sum[v];
 47     }
 48     if(num==sum[u]){//如果標記的次數正好為加的邊的數量,說明這一段在所有邊上都有
 49         ret=max(ret,vv[u]);//標記次數相同的找長度最大的
 50     }
 51 }
 52 
 53 bool pig(int h)//判斷
 54 {
 55     num=0;ret=0; memset(sum,0,sizeof(sum));
 56     for(int i=1;i<=m;i++){
 57         if(len[i]>h){
 58             num++;int lca=LCA(p1[i],p2[i]);
 59             sum[p1[i]]++;sum[p2[i]]++;sum[lca]-=2;
 60         }
 61     }
 62     if(!num) return 1;
 63     Dfs(1,0);return maxx-ret<=h;
 64 }
 65 
 66 int main()
 67 {
 68     scanf("%d%d",&n,&m);
 69     for(int i=1;i<n;i++){
 70         int u,v,w;
 71         scanf("%d%d%d",&u,&v,&w);
 72         add(u,v,w);add(v,u,w);//vv[max(u,v)]=w;
 73     }
 74     dfs(1,0);
 75     for(int i=1;i<=m;i++){
 76         int x,y;
 77         scanf("%d%d",&x,&y);
 78         p1[i]=min(x,y);p2[i]=max(x,y);
 79         int lca=LCA(x,y);
 80         len[i]=dis[x]+dis[y]-2*dis[lca];
 81         maxx=max(maxx,len[i]);
 82     }
 83     int l=0,r=maxx;
 84     while(l<=r){
 85         int mid=(l+r)>>1;//cout<<"mid= "<<mid<<endl;
 86         if(pig(mid))
 87             r=mid-1;
 88         else
 89             l=mid+1;
 90     }
 91     cout<<l<<endl;
 92 
 93 }
 94 /*
 95 6 3
 96 1 2 3
 97 1 6 4
 98 1 3 7
 99 3 4 6
100 3 5 5
101 3 6 
102 2 5 
103 4 5
104 
105 
106 11
107 
108 */

不開心,最近狀態不好,鐵了一年了。唉,菜是原罪。技術分享圖片

洛谷 P2680 運輸計劃-二分+樹上差分(邊權覆蓋)