洛谷 2680 (NOIp2015) 运输计划

题目:https://www.luogu.org/problemnew/show/P2680

因为是最长的时间最短,所以二分!

离线LCA可以知道路径长度。每次只看超过二分值的路径。

原本的想法是遍历一下每条超过二分值的路径,找出“去掉它就能使该路径合法”的那些边,打上标记,最后找到有k个标记的边就能行。

但有点不合适。不需要找出“……”的那些边,而把所有该路径上的边都打上标记,最后找到有k个标记的边的时候判断一下去掉它能不能行。

  这样就像均摊了复杂度一样。反正最后都得遍历树,可以那时多做一点、之前少做一点,使时间更好。

注意给边打标记是把它落在它下面的那个点上。而且是LCA的地方-2,和给点打标记不同。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N=3e5+5;
int n,m,head[N],xnt,cs[N],bh[N],l,r,mx,fa[N],hd[N],xt,dis[N],nw,mid,ans;
bool vis[N],flag;
struct Ques{
    int dis,x,y,lca;
}a[N];
struct Edge{
    int next,to,w;
    Edge(int n=0,int t=0,int w=0):next(n),to(t),w(w) {}
}edge[N<<1],ed[N<<1];
void add(int x,int y,int z)
{
    edge[++xnt]=Edge(head[x],y,z);head[x]=xnt;
    edge[++xnt]=Edge(head[y],x,z);head[y]=xnt;
}
void ad(int x,int y,int z)
{
    ed[++xt]=Edge(hd[x],y,z);hd[x]=xt;
    ed[++xt]=Edge(hd[y],x,z);hd[y]=xt;
}
int find(int a){return fa[a]==a?a:fa[a]=find(fa[a]);}
bool cmp(Ques a,Ques b){return a.dis>b.dis;}
void tarjan(int cr,int f)
{
    vis[cr]=1;
    for(int i=hd[cr],v;i;i=ed[i].next)
        if(vis[v=ed[i].to])
        {
            int k=ed[i].w,lc=find(v);
            a[k].lca=lc;a[k].dis=dis[cr]+dis[v]-2*dis[lc];
            mx=max(mx,a[k].dis);
        }
    for(int i=head[cr],v;i;i=edge[i].next)
        if((v=edge[i].to)!=f)
        {
            bh[v]=i;
            dis[v]=dis[cr]+edge[i].w;
            tarjan(v,cr);fa[v]=cr;
        }
}
void solve(int cr)
{
    cs[a[cr].x]++;cs[a[cr].y]++;cs[a[cr].lca]-=2;
}
int dfs(int cr,int f)
{
    int ret=cs[cr];
    for(int i=head[cr],v;i;i=edge[i].next)
        if((v=edge[i].to)!=f)
        {
            ret+=dfs(v,cr);if(flag)return 0;
        }
    if(ret==nw&&mx-edge[bh[cr]].w<=mid)flag=1;
    return ret;
}
int main()
{
    scanf("%d%d",&n,&m);int x,y,z;
    for(int i=1;i<n;i++)
    {
        scanf("%d%d%d",&x,&y,&z);add(x,y,z);
        fa[i]=i;
    }
    fa[n]=n;
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d",&a[i].x,&a[i].y);ad(a[i].x,a[i].y,i);
    }
    tarjan(1,0);sort(a+1,a+m+1,cmp);r=mx;
    while(l<=r)
    {
        mid=((l+r)>>1);memset(cs,0,sizeof cs);
        for(nw=1;nw<=m&&a[nw].dis>mid;nw++)solve(nw);
        nw--;flag=0;dfs(1,0);
        if(flag)ans=mid,r=mid-1;
        else l=mid+1;
    }
    printf("%d",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/Narh/p/9218679.html