zoukankan      html  css  js  c++  java
  • 图论训练之四

    https://www.luogu.org/problem/P2680

    题意:n个点,n-1条边,边有边权,无向图,m条航线,0时刻同时启程,你可以将一个边权变为0,求使得最后一个航线到达最少花费时间。

    这道题很早就做过了,但现在又忘了,

    而且觉得这是一道好题,所以写一篇博客

    分析;

    首先最大值最小,二分毋庸置疑

    当然是二分答案,但怎么判断就是本题的难点了

    明确,删边一定是在最长的路线中删去(很好理解吧)

    在此基础上

    看能否有其他的路线经过删的边就更好

    普及一下树上差分

    如果是点差分,(a,b),则在a处+1,b处+1,LCA处-1,fa[LCA]处-1

    如果是边差分,(a,b),则在a处+1,b处+1,LCA处-2

    回到正题

    关键在于check函数

    check的时候把所有大于mid值的路径记录下来,找出被所有这样路径覆盖的最长的道路:

    如果没有这样的道路 return false;

    如果这样的道路被减去之后依然大于mid return false

    找出被所有路径覆盖的道路:

    在树中将所有路径起、始权值+1,LCA权值-2,从所有叶节点往上累加(dfs序维护真是好),

    最终权值为路径数的点到其父亲的边为所求边 dis[i]表示i到根的距离;

    tmp[i]表示i这个点通往父亲的边,目的是记录这条边被遍历的次数 ;

    num[i]的作用是找到叶节点向上累加

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    using namespace std;
    const int maxn=300010;
    struct node{
        int to,next,w;
    }edge[maxn*2];
    struct no{
        int u,v,lcaa,diss;
    }lu[maxn*2];
    int summ,cnt=0,k,n,m,num[maxn],mi[maxn],vis[maxn];
    int temp[maxn],head[maxn],deep[maxn],dis[maxn];
    int fa[maxn][25],dp[maxn][25];
    void adde(int u,int v,int w){
        k++;
        edge[k].to=v;
        edge[k].next=head[u];
        edge[k].w=w;
        head[u]=k;
    }
    void dfs(int x,int pa,int dep){
        cnt++;
        num[cnt]=x;
        deep[x]=dep;
        vis[x]=1;
        for(int i=1;i<25;i++){
            fa[x][i]=fa[fa[x][i-1]][i-1];
        }
        for(int i=head[x];i>0;i=edge[i].next){
            int v=edge[i].to;
            if(!vis[v]){
                fa[v][0]=x;
                dis[v]=dis[x]+edge[i].w;
                dfs(v,x,dep+1);
            }
        }
    }
    int lca(int x,int y){
        if(deep[x]<deep[y]) swap(x,y);
        int t=deep[x]-deep[y];
        for(int i=0;i<25;i++){
            if((1<<i)&t) x=fa[x][i];
        }
        if(x==y) return x;
        for(int i=24;i>=0;i--){
            if(fa[x][i]!=fa[y][i]){
                x=fa[x][i];y=fa[y][i];
            }
        }
        return fa[x][0];
    }
    bool check(int mid){
        int cnt=0,ans=0;
        memset(temp,0,sizeof(temp));
        for(int i=1;i<=m;i++){
            if(lu[i].diss>mid){
                temp[lu[i].u]++;temp[lu[i].v]++;temp[lu[i].lcaa]-=2;
                ans=max(ans,lu[i].diss-mid);
                cnt++;
            }
        }
        if(cnt==0) return true;
        for(int i=n;i>=1;i--) temp[fa[num[i]][0]]+=temp[num[i]];
        for(int i=2;i<=n;i++) if(temp[i]==cnt&&dis[i]-dis[fa[i][0]]>=ans) return true;
        return false;
    }
    int main(){
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n-1;i++){
            int x,y,w;
            scanf("%d%d%d",&x,&y,&w);
            adde(x,y,w);adde(y,x,w);
            summ+=w;
        }
        dis[1]=0;
        dfs(1,0,1);
        for(int i=1;i<=m;i++){
            scanf("%d%d",&lu[i].u,&lu[i].v);
            lu[i].lcaa=lca(lu[i].u,lu[i].v);
            lu[i].diss=dis[lu[i].u]+dis[lu[i].v]-2*dis[lu[i].lcaa];
        }
        int left=0,right=summ;
        int mid;
        while(left<right){
            mid=(left+right)>>1;
            if(check(mid)) right=mid;
            else left=mid+1;
        }
        printf("%d",left);
        return 0;
    }
    
  • 相关阅读:
    算法笔记--二分图判定
    算法笔记--最小表示法
    Codeforces 525A
    Codeforces 140D
    Codeforces C
    Codeforces 665C
    Codeforces 604B
    Codeforces 285C
    The Fewest Coins POJ
    Triangular Pastures POJ
  • 原文地址:https://www.cnblogs.com/wzxbeliever/p/11625907.html
Copyright © 2011-2022 走看看