1. 程式人生 > >P2680 運輸計劃 A-二分答案-樹上邊差分

P2680 運輸計劃 A-二分答案-樹上邊差分

  • https://www.luogu.org/problemnew/show/P2680
  • 題意:首先這是一棵n個節點的樹,然後對於樹上的m條鏈,我們可以選取樹上的唯一一條邊使它的邊權變為0
  • 求處理後最長鏈的長度,要求使得最後最長鏈長度最小,最大值最小問題,二分答案
  • 思路:二分答案肯定是二分的時間,然後關鍵是預處理與二分的check怎麼實現。預處理可以通過LCA 求出樹上任意兩點
  • 的距離,然後可以對m條鏈進行一下預處理出每條的長度,check時檢驗哪些邊長度超過了當前列舉的答案,然後對這些
  • 邊進行差分更新,跑一邊dfs更新答案,因為我們只對長度大於當前列舉答案的那些邊進行更新差分陣列,所以為了能夠
  • 最終達到一個合法狀態,刪除的邊至少得滿足(這些長度大於當前答案的鏈 都經過這條邊)所以我們的更新條件為:
  • if(power[edge[i].v]==sp&&edge[i].w>ans)ans=edge[i].w;

  • 求一個經過這條邊的非法的鏈的數目為sp 並且 邊值最大的刪掉是最優的
  • #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    #define maxn 301212
    int n,m,a,b,t,u,v,cnt,c;
    int head[maxn],deep[maxn];
    int dp[maxn][50],power[maxn];
    ll ans,dis[maxn],L,R,mid,sp;
    struct node
    {
        int v,to,w;
    } edge[maxn];
    struct edg
    {
        int x,y,z;
    } orz[maxn];
    struct data
    {
        int u,v,fa;
        ll sum;
        bool operator<(const data&b)const
        {
            return sum>b.sum;
        }
    } lhk[maxn];
    void add(int u,int v,int w)
    {
        edge[++cnt].v=v;
        edge[cnt].to=head[u];
        edge[cnt].w=w;
        head[u]=cnt;
    }
    void dfs(int cur,int fa)
    {
        deep[cur]=deep[fa]+1;
        dp[cur][0]=fa;
        for(int j=1; (1<<j)<=deep[cur]; j++)
            dp[cur][j]=dp[dp[cur][j-1]][j-1];
        for(int i=head[cur]; i!=-1; i=edge[i].to)
        {
            if(edge[i].v==fa)continue;
            dis[edge[i].v]=dis[cur]+edge[i].w;
            dfs(edge[i].v,cur);
        }
    }
    int lca(int x,int y)
    {
        if(deep[x]<deep[y])swap(x,y);
        for(int i=30; i>=0; i--)
            if(deep[y]+(1<<i)<=deep[x])
                x=dp[x][i];
        if(x==y)return x;
        for(int i=30; i>=0; i--)
            if(dp[x][i]!=dp[y][i])
            {
                x=dp[x][i];
                y=dp[y][i];
            }
        return dp[x][0];
    }
    void get(int cur,int fa)
    {
        for(int i=head[cur]; i!=-1; i=edge[i].to)
        {
            if(edge[i].v==fa)continue;
            get(edge[i].v,cur);
            power[cur]+=power[edge[i].v];
            if(power[edge[i].v]==sp&&edge[i].w>ans)ans=edge[i].w;
        }
    }
    bool ok(ll cur)
    {
        memset(power,0,sizeof(power));
        if(lhk[0].sum<=cur)return true;
        bool flag=0;
        sp=ans=0;
        for(int i=0; i<m; i++)
        {
            if(lhk[i].sum<=cur)
                break;
            power[lhk[i].u]++;
            power[lhk[i].v]++;
            power[lhk[i].fa]-=2;
            sp++;
        }
        get(1,0);
        if(lhk[0].sum-ans>cur)return 0;
        return 1;
    }
    int main()
    {
        memset(head,-1,sizeof(head));
        scanf("%d%d",&n,&m);
        for(int i=1; i<n; i++)
        {
            scanf("%d%d%d",&u,&v,&c);
            add(u,v,c);
            add(v,u,c);
            orz[i].x=u;
            orz[i].y=v;
            orz[i].z=c;
        }
        dis[1]=deep[0]=0;
        dfs(1,0);
        for(int i=0; i<m; i++)
        {
            scanf("%d%d",&lhk[i].u,&lhk[i].v);
            int qyn=lca(lhk[i].u,lhk[i].v);
            lhk[i].sum=dis[lhk[i].u]+dis[lhk[i].v]-2*dis[qyn];
            lhk[i].fa=qyn;
        }
        ans=0;
        sort(lhk,lhk+m);
        R=1e12;
        L=0;
        while(L<R)
        {
            mid=(L+R)/2;
            if(ok(mid))R=mid;
            else L=mid+1;
        }
        printf("%lld\n",L);
        return 0;
    }