[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;
}
原文地址:https://www.cnblogs.com/Memory-of-winter/p/7813005.html