洛谷 P2680 運輸計劃-二分+樹上差分(邊權覆蓋)
P2680 運輸計劃
題目背景
公元 20442044 年,人類進入了宇宙紀元。
題目描述
公元20442044 年,人類進入了宇宙紀元。
L 國有 nn 個星球,還有 n-1n−1 條雙向航道,每條航道建立在兩個星球之間,這 n-1n−1 條航道連通了 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-1n−1 行描述航道的建設情況,其中第 ii 行包含三個整數 a_i, b_iai?,bi? 和 t_iti?,表示第 ii 條雙向航道修建在 a_iai? 與 b_ibi? 兩個星球之間,任意飛船駛過它所花費的時間為 t_iti?。數據保證 1 \leq a_i,b_i \leq n1≤ai?,bi?≤n 且 0 \leq t_i \leq 10000≤ti?≤1000。
接下來 mm 行描述運輸計劃的情況,其中第 jj 行包含兩個正整數 u_juj? 和 v_jvj?,表示第 jj 個運輸計劃是從 u_juj? 號星球飛往 v_jvj?號星球。數據保證 1 \leq u_i,v_i \leq n1≤ui?,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 運輸計劃-二分+樹上差分(邊權覆蓋)