洛谷P2680 [NOIP2015 提高组] 运输计划(树上差分+二分)

题目链接
题目大意:给你一颗树,再给定m条路径,你可以使树上一条边的权值变为0,要求这m条路径的最大值最小。
很暴力的想法就是枚举n-1条边挨个删,这样是超时的。
但是看到最大值最小,很容易想到二分答案,然后开始去思考这个最小值是否满足单调性。
很明显,如果完成这m个任务需要a的时间,那么必然存在一个b使得b>a,并且可以用b时间去完成这m个任务。
然后就可以直接去二分这个最小值了,考虑怎么判断答案的正确性。
我们注意到,如果这m条路径中有k条的值是大于我们二分的这个mid的,那么我们归零的这条边必须被这k条边经过,并且归零之后满足所有路径长度小于mid。若不被k条边经过的话,那么我们去掉的边至少有一条路径的长度是没有变化的,依旧是大于mid,所以要求被这k条边经过。
然后考虑怎么快速的修改边被覆盖的次数,树上差分,然后注意到这里修改的是边的覆盖次数,所以我们把边权下放给子结点,然后树剖就可以了,当然也可以倍增,但常数有点大,树剖的话我们可以很自然的利用dfs序去干一些事情。
代码:

#include<bits/stdc++.h>
#define lk (k<<1)
#define rk (k<<1|1)
using namespace std;
typedef long long ll;
const double EPS=1e-8;
const double PI=acos(-1.0);
const int INF=0x3f3f3f3f;
const int N=3e5+10;
inline int read()
{
    
    
    char c;
    int sign=1,res=0;
    c=getchar();
    while(c<'0'||c>'9'){
    
    
        if(c=='-') sign=-1;
        c=getchar();
    }
    while(c>='0'&&c<='9'){
    
    
        res=res*10+c-'0';
        c=getchar();
    }
    return sign*res;
}struct edge
{
    
    
    int v,next,w;
}e[N<<1];
int head[N],cnt;
void add(int u,int v,int w)
{
    
    
    e[++cnt].v=v;
    e[cnt].w=w;
    e[cnt].next=head[u];
    head[u]=cnt;
}
int n,m;
void init()
{
    
    
    n=read();
    m=read();
    for(int i=1;i<n;i++){
    
    
        int u,v,w;
        u=read();
        v=read();
        w=read();
        add(u,v,w);
        add(v,u,w);
    }
}
int top[N],dfn[N],len,dis[N],l[N],id[N];
int fa[N],dep[N],size[N],son[N];
void dfs1(int u,int p)
{
    
    
    dep[u]=dep[p]+1;
    son[u]=0;
    size[u]=1;
    fa[u]=p;
    for(int i=head[u];i;i=e[i].next)
    {
    
    
        int v=e[i].v;
        if(v==p) continue;
        dis[v]+=dis[u]+e[i].w;
        l[v]=e[i].w;
        dfs1(v,u);
        size[u]+=size[v];
        if(size[v]>size[son[u]]) son[u]=v;
    }
}
void dfs2(int u,int p)
{
    
    
    dfn[u]=++len;
    id[len]=u;
    if(son[u])
    {
    
    
        top[son[u]]=top[u];
        dfs2(son[u],u);
    }
    for(int i=head[u];i;i=e[i].next)
    {
    
    
        int v=e[i].v;
        if(v==p) continue;
        if(!top[v])
        {
    
    
            top[v]=v;
            dfs2(v,u);
        }
    }
}
int lca(int u,int v)
{
    
    
    int fu=top[u],fv=top[v];
    while(fu!=fv)
    {
    
    
        if(dep[fu]<dep[fv]) swap(fu,fv),swap(u,v);
        u=fa[fu];fu=top[u];
    }
    if(dep[u]>dep[v]) swap(u,v);
    return u;
}
int x[N],y[N],val[N],cf[N],z[N],maxw;
bool judge(int mid)
{
    
    
    memset(cf,0,sizeof(cf));
    int k=0;
    for(int i=1;i<=m;i++)
    {
    
    
        if(val[i]>mid)
        {
    
    
            cf[x[i]]++;
            cf[y[i]]++;
            cf[z[i]]-=2;
            k++;
        }
    }
    for(int i=n;i>=1;i--)
    {
    
    
        cf[fa[id[i]]]+=cf[id[i]];
        if(cf[id[i]]==k&&maxw-l[id[i]]<=mid) return true;
    }
    return false;
}
int main()
{
    
    
    init();
    dfs1(1,0);
    top[1]=1;
    dfs2(1,0);
    int r=-1,l=0;
    for(int i=1;i<=m;i++)
    {
    
    
        x[i]=read();
        y[i]=read();
        z[i]=lca(x[i],y[i]);
        val[i]=dis[x[i]]+dis[y[i]]-2*dis[z[i]];
        maxw=max(maxw,val[i]);
    }
    r=maxw;
    int ans,mid;
    while(l<=r)
    {
    
    
        mid=(l+r)>>1;
        if(judge(mid)) ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/amazingee/article/details/112970802
今日推荐