洛谷P2680 运输计划——题解

题目传送门
题目大意:
有一颗n个节点的树和m条路径,你可以将树上任一条边的长度变为0,求所有路径中最长的一条的最小值。


思考过程:
这是典型的求最大值最小可以为多少的问题,我们考虑二分答案。
然后我们考虑如何检验当前答案是否可行。显然,长度小于当前答案的路径都是可行的,问题转化为如何判断长度大于当前答案的路径是否可行。因为我们要删掉一条边使得这些路径长度都变小,所以我们肯定要删掉这些路径的交,而且要删掉交里面长度最大的那条边。而这可以用差分来解决。


具体做法:
我们对于每条长度超过当前答案的路径的两个端点(假设为x,y)求lca,然后book[x]++,book[y]++,book[lca]-=2。在按dfs序从后往前把儿子的book加到父亲上,最后book值等于超过当前答案路径的条数的就是要检验的,求他们的最大值就好。如果最大值大于等于原最长路径长度-当前答案,则当前答案可行。


代码:

#include 
using namespace std;

const int maxn=3e5+1000;
struct stu
{
    int to,next,dis;	
}road[maxn*2]; int first[maxn],cnt;
int dep[maxn],cnum[maxn],fa[maxn],val[maxn],hson[maxn],top[maxn],id[maxn],dfn[maxn],book[maxn];
int u[maxn],v[maxn],len[maxn];
int nowtop,n,maxx,ans,mid,m;

void addedge(int x,int y,int dis1)
{
    road[++cnt].to=y;
    road[cnt].next=first[x];
    first[x]=cnt;
    road[cnt].dis=dis1;	
}

void dfs1(int now,int depth)
{
    int max1=0;
    dep[now]=depth;
    cnum[now]=1;
    for(int i=first[now];i;i=road[i].next)
    {
        int to=road[i].to;
        if(to==fa[now]) continue;
        fa[to]=now;
        val[to]=road[i].dis;
        dfs1(to,depth+1);
        cnum[now]+=cnum[to];
        if(cnum[to]>max1) { hson[now]=to;max1=cnum[to]; }
    }
}

void dfs2(int now,int what)
{
    if(what==1) nowtop=now;
    top[now]=nowtop;
    id[now]=++cnt;
    dfn[cnt]=now;
    if(!hson[now]) return;
    dfs2(hson[now],0);
    for(int i=first[now];i;i=road[i].next)
    {
        int to=road[i].to;	
        if(to==fa[now]||to==hson[now]) continue;
        dfs2(to,1);
    }
}

int get_lca(int x,int y)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]]) swap(x,y);
        x=fa[top[x]];
    }
    if(dep[x]<dep[y]) swap(x,y);
    return y;
}

struct seg1
{
    int seg[maxn*5];
    
    void build(int note,int l,int r)
    {
        if(l>r) return;
        if(l==r) { seg[note]=val[dfn[l]];return; }
        int mid=(l+r)>>1;
        build(note*2,l,mid);build(note*2+1,mid+1,r);
        seg[note]=seg[note*2]+seg[note*2+1];
    }
    
    int query(int note,int l,int r,int wantl,int wantr)
    {
        if(l>wantr||r<wantl) return 0;
        if(l>=wantl&&r<=wantr) return seg[note];
        int mid=(l+r)>>1;
        return query(note*2,l,mid,wantl,wantr)+query(note*2+1,mid+1,r,wantl,wantr);	
    }

    int get_ans(int x,int y)
    {
        int nowans=0;
        while(top[x]!=top[y])
        {
            if(dep[top[x]]<dep[top[y]]) swap(x,y);
            nowans+=query(1,1,n,id[top[x]],id[x]);
            x=fa[top[x]];
        }
        if(dep[x]<dep[y]) swap(x,y);
        nowans+=query(1,1,n,id[y],id[x]);
        nowans-=val[y];
        return nowans;
    }
    
}T1;

void work(int x,int y)
{
    int lca1=get_lca(x,y);
    book[x]++;book[y]++;book[lca1]-=2;	
}

bool judge()
{
    if(maxx<=mid) return 1;
    memset(book,0,sizeof(book));
    int cntt=0,want=maxx-mid;
    for(int i=1;i<=m;i++)
    {
        if(len[i]>mid)
        {
            cntt++;
            work(u[i],v[i]);
        }
    }
    for(int i=n;i>=1;i--) book[fa[dfn[i]]]+=book[dfn[i]];
    int nowmax=0;
    for(int i=1;i<=n;i++)
        if(book[i]>=cntt) nowmax=max(nowmax,val[i]);
    if(nowmax>=want) return 1;
    return 0;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n-1;i++)
    {
        int x,y,z;
        scanf("%d%d%d",&x,&y,&z);
        addedge(x,y,z);addedge(y,x,z);	
    }
    dfs1(1,1);
    cnt=0;
    dfs2(1,1);
    T1.build(1,1,n);
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&u[i],&v[i]);
        if(dep[u[i]]<dep[v[i]]) swap(u[i],v[i]);
        len[i]=T1.get_ans(u[i],v[i]);
        ans=max(ans,len[i]);
        maxx=ans;
    }
    int l=0,r=ans;
    while(l<=r)
    {
        mid=(l+r)>>1;
        bool kk=judge();
        if(kk) { ans=mid;r=mid-1; }
        else l=mid+1;
    }
    printf("%d\n",ans);
    return 0;	
}

你可能感兴趣的:(题解)