Luogu P2680 [NOIp提高組2015]運輸計劃


題目描述 傳送門
L 國有 n 個星球,還有 n-1 條雙向航道,每條航道建立在兩個星球之間,這 n-1 條航道連通了 L 國的所有星球。
小 P 掌管一家物流公司,該公司有很多個運輸計劃,每個運輸計劃形如:有一艘物
流飛船需要從 ui 號星球沿最快的宇航路徑飛行到 vi 號星球去。顯然,飛船駛過一條航道 是需要時間的,對於航道 j,任意飛船駛過它所花費的時間爲 tj,並且任意兩艘飛船之 間不會產生任何干擾。
爲了鼓勵科技創新,L 國國王同意小 P 的物流公司參與 L 國的航道建設,即允許小 P 把某一條航道改造成蟲洞,飛船駛過蟲洞不消耗時間。
在蟲洞的建設完成前小 P 的物流公司就預接了 m 個運輸計劃。在蟲洞建設完成後, 這 m 個運輸計劃會同時開始,所有飛船一起出發。當這 m 個運輸計劃都完成時,小 P 的 物流公司的階段性工作就完成了。
如果小 P 可以自由選擇將哪一條航道改造成蟲洞,試求出小 P 的物流公司完成階段時要的最短時間是多少?


樹鏈剖分+線段樹暴力嘗試修改所有路徑40分,,,
正解:二分答案+樹上差分+LCA(我用的樹鏈剖分)
大致思路是:先把無根樹轉化成有根樹,預處理lca和運輸的路徑長,然後二分答案,對於大於二分答案的路徑,需要嘗試刪除其中一條邊來滿足答案,易知需要刪除的邊一定是這些路徑的交集,利用樹上差分求出每條邊被多少條大於答案的路徑覆蓋,方法是把一條(u,v) 路徑分成兩條(u,lca(u,v)),(v,lca(u,v)) 的鏈,類似差分數列,給最前面的+1,超尾位置-1,前綴和就是實際值,那麼就是sum[u]–,sum[v]–,sum[lca(u,v)]-=2;最後根據dfs序遞推出前綴和,貪心嘗試刪除最大的那條交邊,繼續二分直到不能分。
代碼

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=3e5+10;
int n,m;
struct Edge{
    int to,nxt,d;
    Edge(int a=0,int b=0,int c=0):to(a),nxt(b),d(c){}
}v[maxn*2];
int len=0,cnt=0,l=-1,r=0;
int h[maxn],fa[maxn],sz[maxn],son[maxn],lca[maxn],seq[maxn],dist[maxn],cost[maxn],depth[maxn],top[maxn],s[maxn],t[maxn],sum[maxn];
void dfs1(int u,int f){
    fa[u]=f;
    sz[u]=1;
    son[u]=0;
    depth[u]=depth[f]+1;
    for(int i=h[u];i;i=v[i].nxt) if(v[i].to!=f){
        dist[v[i].to]=dist[u]+v[i].d;
        dfs1(v[i].to,u);
        sz[u]+=sz[v[i].to];
        if(sz[v[i].to]>sz[son[u]]) son[u]=v[i].to;
    }
    seq[len++]=u;
}
void dfs2(int u,int tp){
    top[u]=tp;
    if(sz[u]>1) dfs2(son[u],tp);
    for(int i=h[u];i;i=v[i].nxt) if(v[i].to!=fa[u]&&v[i].to!=son[u])
        dfs2(v[i].to,v[i].to);
}
int getlca(int x,int y){
    int f1=top[x],f2=top[y],ans=0;
    while(f1!=f2){
        if(depth[f1]<depth[f2]) swap(f1,f2),swap(x,y);
        x=fa[f1];
        f1=top[x];        
    }
    if(x==y) return x;
    if(depth[x]<depth[y]) swap(x,y);
    return y;
}
bool check(int a){
    memset(sum,0,sizeof(sum));
    int tot=0,ans=0;
    for(int i=0;i<m;i++) if(cost[i]>a){
        sum[lca[i]]-=2;
        sum[s[i]]++;
        sum[t[i]]++;
        tot++;
    }
    for(int i=0;i<len;i++) sum[fa[seq[i]]]+=sum[seq[i]];
    for(int i=2;i<=n;i++) if(sum[i]==tot) ans=max(ans,dist[i]-dist[fa[i]]);
    for(int i=0;i<m;i++) if(cost[i]-ans>a) return false;
    return true;
}
int main(){
    scanf("%d%d",&n,&m);
    memset(h,0,sizeof(h));
    for(int i=1;i<n;i++){
        int a,b,w;
        scanf("%d%d%d",&a,&b,&w);
        v[++cnt]=Edge(b,h[a],w);
        h[a]=cnt;
        v[++cnt]=Edge(a,h[b],w);
        h[b]=cnt;
        r+=w;
    }
    depth[1]=sz[0]=dist[1]=0;
    dfs1(1,1);
    dfs2(1,1);
    for(int i=0;i<m;i++){
        scanf("%d%d",&s[i],&t[i]);
        lca[i]=getlca(s[i],t[i]);
        cost[i]=dist[s[i]]+dist[t[i]]-2*dist[lca[i]];
    }
    while(l<r-1){
        int mid=l+r>>1;
        if(check(mid)) r=mid;
        else l=mid;
    }
    printf("%d\n",r);
    return 0;
}
發佈了59 篇原創文章 · 獲贊 4 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章