• NOIP2015 运输计划


    题目大意:

    给定一棵有n个节点的有边权树,给定m个运输任务,每个任务从x出发到y,耗时就是树上两点之间的距离。所有运输任务同时开始,总耗时就是最大耗时。

    现给一次机会,使某个树边的权值变为0,请选择合适树边,使得处理后,总耗时最小。

    输出该最小值。

    n<=300000,m<=300000

    分析:

    暴力60分易得,nmlogn,暴力枚举变权值边即可。

    正解:

    tarjanLCA + 二分答案 + 树上差分 + 贪心思想

    0.离线预处理每个询问两点之间LCA,用tarjan,树剖会卡掉。从而处理出了任意两点之间距离。

    1.发现单调性,二分这个最小答案,判断。

    2.判断的时候,先找到所有dis值>mid的任务,它们肯定是要通过消掉一条公共边,使得合法的。

    3.这个时候用贪心,设len为所有dis中最大值,合法的时候,贪心的要使去掉的公共边是所有公共边中最长的。len-k<=mid才合法。

    4.所以要找到k。将所有这次的要处理的tot个任务,放在树上差分。dfs pushup即可。当一个点的权值=tot的时候,它与它父亲之间的边就是一条合法的公共边,所有公共边之间求最大值即可。

    一切操作的正确性都易证明。

    #include<bits/stdc++.h>
    #define num ch-'0'
    using namespace std;
    const int N=300000+10;
    const int inf=0x3f3f3f3f;
    void read(int &x)
    {
        x=0;char ch;
        while(!isdigit(ch=getchar()));
        for(x=num;isdigit(ch=getchar());x=x*10+num);
    }
    int n,m;
    struct node{
        int nxt,to,val;
    }bian[2*N],edge[2*N];
    int hd[N],cnt;
    int pre[N],cc;
    bool vis[N];
    int dis[N];
    int w[N];
    struct que{
        int x,y;
        int lca;
        int dist; 
    }q[N];
    int ans;
    int L,R;
    int mx,tot;
    void add1(int x,int y,int z)//树边 
    {
        bian[++cnt].nxt=hd[x];
        bian[cnt].to=y;
        bian[cnt].val=z;
        hd[x]=cnt;
    }
    void add2(int x,int y,int z)//询问边 
    {
        edge[++cc].nxt=pre[x];
        edge[cc].to=y;
        edge[cc].val=z;
        pre[x]=cc;
    }
    int ff[N];
    int fin(int x)//并查集 
    {
        if(ff[x]==x) return x;
        return ff[x]=fin(ff[x]);
    }
    void tarjan(int x)//tarjan求 LCA 
    {
        ff[x]=x;
        vis[x]=1;
        for(int i=pre[x];i;i=edge[i].nxt)
        {
            int y=edge[i].to;
            if(vis[y])
            {
                q[edge[i].val].lca=fin(y);
            }
        }
        for(int i=hd[x];i;i=bian[i].nxt)
        {
            int y=bian[i].to;
            if(!vis[y])
            {
                tarjan(y);
                ff[y]=x;
            }
        }
    }
    void dfs1(int x,int f,int d,int ds)//处理dis,到根节点距离 
    {
        dis[x]=ds;
        for(int i=hd[x];i;i=bian[i].nxt)
        {
            int y=bian[i].to;
            if(y==f) continue;
            dfs1(y,x,d+1,ds+bian[i].val);
        }    
    }
    void dfs3(int x,int f,int df)//树上差分 
    {
        for(int i=hd[x];i;i=bian[i].nxt)
        {
            int y=bian[i].to;
            if(y==f) continue;
            dfs3(y,x,bian[i].val);
            w[x]+=w[y];
        }
        if(w[x]==tot){
            mx=max(mx,df);
        }
    }
    
    bool binary(int x)//二分 
    {    
        memset(w,0,sizeof w);
        int len=0;
        tot=0;
        mx=0;
        for(int i=1;i<=m;i++)
        {
            if(q[i].dist>x)
            {
                tot++;
                w[q[i].x]++,w[q[i].y]++,w[q[i].lca]-=2;    
                len=max(len,q[i].dist);
            }    
        }
        dfs3(1,-1,0);
        if(len-mx<=x) return true;
        else return false;
    }
    int main()
    {
        read(n);read(m);
        int x,y,z;
        for(int i=1;i<=n-1;i++)
        {
            read(x);read(y);read(z);
            add1(x,y,z);
            add1(y,x,z);
        }
        dfs1(1,-1,1,0);
        
        for(int i=1;i<=m;i++)
        {
            read(x);read(y);
            q[i].x=x,q[i].y=y;
            add2(x,y,i),add2(y,x,i);
        }
           tarjan(1);
           
        for(int i=1;i<=m;i++)
        { 
        q[i].dist=dis[q[i].x]+dis[q[i].y]-2*dis[q[i].lca];
        R=max(R,q[i].dist);
        }
        R+=10;
        L=0;
        ans=inf;
        while(L<=R)//二分答案 
        {
            int mid=(L+R)/2;
            
            bool fll=binary(mid);
            
            if(fll) {
                ans=min(ans,mid);R=mid-1;
            }
            else{
                L=mid+1;
            }
        }
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    python 赋值操作的知识点
    python while循环语句
    python dict遍历
    python列表的切片操作
    Python做下载器需要掌握哪些
    BeautifulSoup已经安装,但仍提示No module named
    python 列表循环输出中文
    python 字符串split (string split)
    python 调用解释器
    分享python字符串去除空格的知识点
  • 原文地址:https://www.cnblogs.com/Miracevin/p/9069936.html
Copyright © 2020-2023  润新知