zoukankan      html  css  js  c++  java
  • [NOIP2015 TG D2T3]运输计划

    题目大意: 给你一棵n个节点的树,有边权,有多个任务,每个要求从ui号节点到 vi号节点去。m 个计划, 这 m 个计划会同时开始。当这 m 个任务都完成时,工作完成。

    现在可以把任意一个边的边权变为0,试求出完成工作所需要的最短时间是多少?

    题解:先求出每个任务原来的所需时间,一种想法是枚举改变哪条边,但这肯定会超时(不然怎么是T3),然后我们可以想到,我们可以发现必须把最长的时间缩短,才可以把答案时间缩短,于是我们可以用二分它完成的时间,然后进行判断,把时间超过这个二分答案的任务记录一下,然后可以发现,我们要减少其中所有任务都经过的边才可以把所有任务的时间变短。

    那该怎么做呢?就要用树上差分了(节点版),我们用一个数组存每个节点的差分值,它的子树的值的和就是它所要经过的次数,假设任务是从a->b,就可以s[a]++,s[b]++,s[lca(a,b)]-=2(lca要存下来,不然也会超时),具体可以baidu。

    C++ Code:

    #include<cstdio>
    #include<cstring>
    #define maxn 300100
    #define maxm 21
    #define inf 0x7ffffff
    using namespace std;
    int cnt,head[maxn];
    int n,m,ans=inf;
    int s[maxn],e[maxn];
    int lca[maxn],deep[maxn],fa[maxn][maxm],sum[maxn],sum2[maxn];
    int p[maxn];
    struct Edge{
        int to,nxt,cost;
    }edge[maxn<<1];
    void add(int a,int b,int c){
        cnt++;
        edge[cnt].to=b;
        edge[cnt].nxt=head[a];
        edge[cnt].cost=c;
        head[a]=cnt;
    }
    void dfs(int root){
        for (int i=head[root];i;i=edge[i].nxt){
            int ne=edge[i].to;
            if (deep[ne]==0){
                deep[ne]=deep[root]+1;
                sum[ne]=sum[root]+edge[i].cost;
                fa[ne][0]=root;
                dfs(ne);
            }
        }
    }
    void init(){
        for (int i=1;i<maxm;i++){
            for (int j=1;j<=n;j++){
                fa[j][i]=fa[fa[j][i-1]][i-1];
            }
        }
    }
    int LCA(int x,int y){
        if (deep[x]<deep[y])x^=y^=x^=y;
        for (int i=maxm-1;i>=0;i--){
            if (deep[fa[x][i]]>=deep[y]){
                x=fa[x][i];
            }
        }
        if (x==y)return x;
        for (int i=maxm-1;i>=0;i--){
            if (fa[x][i]!=fa[y][i]){
                x=fa[x][i];
                y=fa[y][i];
            }
        }
        return fa[x][0];
    }
    void dfs1(int root){
        if (head[root]==0)return;
        for (int i=head[root];i;i=edge[i].nxt){
            int ne=edge[i].to;
            if (deep[ne]==deep[root]+1){
                dfs1(ne);
                p[root]+=p[ne];
            }
        }
    }
    bool check(int mid){
        int maxo=0,num=0;
        memset(p,0,sizeof(p));
        for (int i=1;i<=n;i++){
            if (sum2[i]>mid){
                num++;
                if (maxo<(sum2[i]-mid))maxo=sum2[i]-mid;
                p[s[i]]++;
                p[e[i]]++;
                p[lca[i]]-=2;
            }
        }
        dfs1(1);
        for (int i=1;i<=n;i++){
            if (p[i]==num){
                if (sum[i]-sum[fa[i][0]]>=maxo)return 1;
            }
        }
        return 0;
    }
    int main(){
        scanf("%d%d",&n,&m);
        for (int i=1;i<n;i++){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            add(a,b,c);
            add(b,a,c);
        }
        deep[1]=1;
        dfs(1);
        init();
        int r=0;
        for (int i=1;i<=m;i++){
            scanf("%d%d",&s[i],&e[i]);
            lca[i]=LCA(s[i],e[i]);
            sum2[i]=(sum[s[i]]+sum[e[i]]-(sum[lca[i]]<<1));
            if(sum2[i]>r)r=sum2[i];
        }
        int l=0;
        while (l<=r){
            int mid=l+r>>1;
            if (check(mid)){
                ans=mid;
                r=mid-1;
            }else{
                l=mid+1;
            }
        }
        printf("%d
    ",ans);
        return 0;
    }
    
  • 相关阅读:
    读取手机联络人实例
    MotionEvent中getX()和getRawX()的区别
    Android开源项目发现--- 效率开发工具篇(持续更新)
    开发资源收藏
    ViewHolder VS HolderView ?
    如何测试 Android 中的定时事件
    性能优化实例
    lamba
    并行操作多个序列map
    连续处理函数reduce
  • 原文地址:https://www.cnblogs.com/Memory-of-winter/p/7813005.html
Copyright © 2011-2022 走看看