zoukankan      html  css  js  c++  java
  • 运输计划

    传送门

    解法:

    首先要学会求 树的最近公共祖先(LCA)

    没用树剖
    用了一个经常可以代替树剖的方法
    树上差分 这个方法很优秀 一定要掌握

    首先 有了lca 就可以求树上两点间长度
    (d[x])(x) 到 根节点 的距离
    (u),(v) 间距离为 (d[u]+d[v]-2*d[lca(u,v)])

    同时可以实现差分
    (ver[x])(x) 的差分数组
    对于 (u),(v) 连边 覆盖原图边的差分处理 (ver[u]++,ver[v]++,ver[lca(u,v)]-=2)
    在进行一次dfs就求到 每个点x到父节点的边 被覆盖的次数ver[x]

    二分答案

    对于每个(u),(v)若距离>二分的值
    说明此时要在其覆盖的边中减去一条满足条件的
    差分处理

    设此时有cnt个路径要删边
    那么能达到此时二分答案则必须有一条边被所有路径覆盖
    即dfs后 存在(ver[x]==cnt)且此时删去改边后 最长的一条路径也要不大于此时答案
    此答案才可行

    最后二分求出的最大可行值即为答案

    代码:

    第一种 倍增法求lca O(nlogn+mlogm+(n+m)logSum) 可能会被卡掉一两个点 开O2能过

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #include<cmath>
    #include<queue>
    #include<map>
    #define inf 2000000000
    #define min(x,y) ((x)<(y)?(x):(y))
    #define max(x,y) ((x)>(y)?(x):(y))
    #define rep(i,a,b) for(int i=(a);i<=(b);++i)
    #define dwn(i,a,b) for(int i=(a);i>=(b);--i)
    using namespace std;
    typedef long long ll;
    int n,m,sum=0,ans;
    int root=1,dep[300010],f[300010][20],lg2[300010],d[300010],ver[300010];
    int tot=1,head[300010];
    struct node
    {
        int u,v,lca,dis;
    }qu[300010];
    struct EDGE
    {
        int to,nxt,d;
    }edge[600010];
    queue<int> que;
    inline void add(int u,int v,int d)
    {
        edge[++tot].to=v;
        edge[tot].nxt=head[u];
        edge[tot].d=d;
        head[u]=tot;
    }
    inline void bfs()
    {
        dep[root]=1;
        que.push(root);
        while(!que.empty())
        {
            int x=que.front();que.pop();
            for(int i=head[x];i;i=edge[i].nxt)
            {
                int y=edge[i].to;
                if(dep[y]) continue;
                f[y][0]=x;dep[y]=dep[x]+1;d[y]=d[x]+edge[i].d;
                rep(j,1,lg2[dep[y]])
                {
                    f[y][j]=f[f[y][j-1]][j-1];
                }
                que.push(y);
            }
        }
    }
    inline int lca(int x,int y)
    {
        if(dep[x]>dep[y]) swap(x,y);
        dwn(i,lg2[dep[y]-dep[x]],0)
            if(dep[f[y][i]]>=dep[x]) y=f[y][i];
        if(x==y) return x;
        dwn(i,lg2[dep[x]],0)
            if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
        return f[x][0];
    }
    void dfs(int x)
    {
        for(int i=head[x];i;i=edge[i].nxt)
        {
            int y=edge[i].to;
            if(dep[x]>dep[y]) continue;
            dfs(y);
            ver[x]+=ver[y];
        }
    }
    bool check(int mid)
    {
        memset(ver,0,sizeof(ver));
        int cnt=0,maxn=0;
        rep(i,1,m)
        {
            if(qu[i].dis>mid)
            {
                cnt++;
                ver[qu[i].u]++,ver[qu[i].v]++,ver[qu[i].lca]-=2;
                maxn=max(maxn,qu[i].dis-mid);
            }
        }
        if(cnt==0) return 1;
        dfs(root);
        rep(i,1,n)
            if(i!=root&&ver[i]==cnt&&d[i]-d[f[i][0]]>=maxn) return 1;
        return 0;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        rep(i,2,n)
        {
            lg2[i]=lg2[i>>1]+1;
        }
        rep(i,1,n-1)
        {
            int u,v,d;
            scanf("%d%d%d",&u,&v,&d);
            add(u,v,d),add(v,u,d);
            sum+=d;
        }
        bfs();
        rep(i,1,m)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            qu[i].lca=lca(u,v);
            qu[i].dis=d[u]+d[v]-2*d[qu[i].lca];
            qu[i].u=u,qu[i].v=v;
        }
        int l=0,r=sum;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(check(mid)) r=mid-1,ans=mid;
            else l=mid+1;
        }
        printf("%d
    ",ans);
        return 0;
    }
    
    

    第二种 tarjan的lca算法 O(n+m+(n+m)logSum) 相对快一些 不用O2过

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<vector>
    #include<cmath>
    #include<queue>
    #include<map>
    #define inf 2000000000
    #define min(x,y) ((x)<(y)?(x):(y))
    #define max(x,y) ((x)>(y)?(x):(y))
    #define rep(i,a,b) for(int i=(a);i<=(b);++i)
    #define dwn(i,a,b) for(int i=(a);i>=(b);--i)
    using namespace std;
    typedef long long ll;
    typedef pair<int,int> P;
    int n,m,sum=0,ans;
    int root=1,d[300010],fa[300010],ver[300010];
    int tot=1,head[300010];
    int v[300010],par[300010];
    struct node
    {
        int u,v,lca,dis;
    }qu[300010];
    struct EDGE
    {
        int to,nxt,d;
    }edge[600010];
    queue<int> que;
    vector<P> ques[300010];
    inline void add(int u,int v,int d)
    {
        edge[++tot].to=v;
        edge[tot].nxt=head[u];
        edge[tot].d=d;
        head[u]=tot;
    }
    inline void bfs()
    {
        que.push(root);
        fa[root]=root;
        while(!que.empty())
        {
            int x=que.front();que.pop();
            for(int i=head[x];i;i=edge[i].nxt)
            {
                int y=edge[i].to;
                if(fa[y]) continue;
                d[y]=d[x]+edge[i].d;
                fa[y]=x;
                que.push(y);
            }
        }
    }
    int get(int x)
    {
        if(x==par[x]) return x;
        return par[x]=get(par[x]);
    }
    void tarjan(int x)
    {
        v[x]=1;
        for(int i=head[x];i;i=edge[i].nxt)
        {
            int y=edge[i].to;
            if(v[y]) continue;
            tarjan(y);
            par[y]=x;
        }
        for(int i=0;i<ques[x].size();++i)
        {
            if(v[ques[x][i].first]==2)
                qu[ques[x][i].second].lca=get(ques[x][i].first);
        }
        v[x]=2;
    }
    void dfs(int x)
    {
        for(int i=head[x];i;i=edge[i].nxt)
        {
            int y=edge[i].to;
            if(fa[y]!=x) continue;
            dfs(y);
            ver[x]+=ver[y];
        }
    }
    bool check(int mid)
    {
        memset(ver,0,sizeof(ver));
        int cnt=0,maxn=0;
        rep(i,1,m)
        {
            if(qu[i].dis>mid)
            {
                cnt++;
                ver[qu[i].u]++,ver[qu[i].v]++,ver[qu[i].lca]-=2;
                maxn=max(maxn,qu[i].dis-mid);
            }
        }
        if(cnt==0) return 1;
        dfs(root);
        rep(i,1,n)
            if(i!=root&&ver[i]==cnt&&d[i]-d[fa[i]]>=maxn) return 1;
        return 0;
    }
    int main()
    {
        scanf("%d%d",&n,&m);
        rep(i,1,n-1)
        {
            int u,v,d;
            scanf("%d%d%d",&u,&v,&d);
            add(u,v,d),add(v,u,d);
            sum+=d;
        }
        bfs();
        rep(i,0,n) par[i]=i;
        rep(i,1,m)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            qu[i].u=u,qu[i].v=v;
            if(u==v) qu[i].lca=u,qu[i].dis=0;
            else ques[u].push_back(P(v,i)),ques[v].push_back(P(u,i));
        }
        tarjan(root);
        rep(i,1,m) qu[i].dis=d[qu[i].u]+d[qu[i].v]-2*d[qu[i].lca];
        int l=0,r=sum;
        while(l<=r)
        {
            int mid=(l+r)>>1;
            if(check(mid)) r=mid-1,ans=mid;
            else l=mid+1;
        }
        printf("%d
    ",ans);
        return 0;
    }
    
    
  • 相关阅读:
    死锁篇
    java线程池
    sql server 多行数据指定字符串拼接
    动态提交 表格
    ABP
    DDD学习
    sql 语句插入数据返回id
    Post方式提交,通过上下文HttpContext,Request[""]获取
    JQ的过滤隐藏
    sql 查询有多少行
  • 原文地址:https://www.cnblogs.com/MYsBlogs/p/11116687.html
Copyright © 2011-2022 走看看