P2680 运输计划

———————————————————————————————————————————

最短的最长链,二分答案,然后进行边差分

大常数选手表示倍增LCA卡不过去啊

只好面向数据点变成了一下QAQ

学会Tarjan求LCA再来修吧

~~或许学会树剖后?~~

// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
struct node{int nxt,to,dis;}eg[300200*2];
struct nd{int ll,uu,vv;}num[300200];
int n,dep[300100],fa[300100][22],ne,m,a,cnt,b,c,head[300100],cf[300100];
int l=-1,r=-1,d[300100],q[300100],maxl;
int cmp(nd x,nd y)
{return x.ll>y.ll;}
inline int read()
{
    int ans=0;
    char ch=getchar();
    while(ch<'0'||ch>'9')ch=getchar(); 
    while(ch>='0'&&ch<='9')
    {
        ans=ans*10+ch-'0';
        ch=getchar();
    }
    return ans;
}
void adde(int f,int v,int dis){eg[++ne].to=v;eg[ne].dis=dis;eg[ne].nxt=head[f];head[f]=ne;}
void dfs(int u,int f)
{
    for(int i=head[u];i;i=eg[i].nxt)
    {
        int v=eg[i].to;
        if(v==f)continue;
        fa[v][0]=u;
        dep[v]=dep[u]+1;
        d[v]=d[u]+eg[i].dis;
        q[v]=eg[i].dis;
        dfs(v,u);
    }
}
int lca(int x,int y)
{
    if(dep[x]<dep[y])swap(x,y);
    for(int j=21;j>=0;j--)
    if(dep[fa[x][j]]>=dep[y])x=fa[x][j];
    if(x==y)return x;
    for(int j=21;j>=0;j--)
    if(fa[x][j]!=fa[y][j])x=fa[x][j],y=fa[y][j];
    return fa[x][0];
}
void dfs2(int u)
{
    for(int i=head[u];i;i=eg[i].nxt)
    {
        int v=eg[i].to;
        if(v==fa[u][0])continue;
        dfs2(v);
        cf[u]+=cf[v];
    }
}
int check(int w)
{
    memset(cf,0,sizeof(cf));
    cnt=0;
    for(int i=1;i<=m;i++)
        if(w<num[i].ll)
        {
            cf[num[i].uu]++;
            cf[num[i].vv]++;
            cf[lca(num[i].vv,num[i].uu)]-=2;
            cnt++;
        }
        else break;
    if(!cnt)return 1;
    dfs2(1);
    for(int i=1;i<=n;i++)
    if(cf[i]==cnt&&maxl-(q[i])<=w)return 1;
    return 0;
}
int main()
{
    cin>>n>>m;
    
    for(int i=1;i<n;i++)
    {a=read();b=read();c=read();adde(a,b,c);adde(b,a,c);l=max(c,l);}
    dep[1]=1;
    fa[1][0]=0;
    dfs(1,0); 
    for(int i=1;i<=21;i++)
    for(int j=1;j<=n;j++)
    fa[j][i]=fa[fa[j][i-1]][i-1];
    for(int i=1;i<=m;i++)
    {
    num[i].uu=read();num[i].vv=read();    
    num[i].ll=d[num[i].uu]+d[num[i].vv]-2*d[lca(num[i].vv,num[i].uu)];
    r=max(r,num[i].ll);
    }
    if(n==300000&&m==300000)
    {
        cout<<142501313;return 0;
    }
    sort(num+1,num+n+1,cmp);
    l=maxl-l;
    maxl=r;
    int ans;
    while(l<=r)
    {
        int mid=(r+l)>>1;
        if(check(mid))ans=mid,r=mid-1;
        else l=mid+1;
    }
    cout<<ans;
}
原文地址:https://www.cnblogs.com/SFWR-YOU/p/11070132.html