NOIP2015 运输计划

题目大意:

给定一棵有n个节点的有边权树,给定m个运输任务,每个任务从x出发到y,耗时就是树上两点之间的距离。所有运输任务同时开始,总耗时就是最大耗时。

现给一次机会,使某个树边的权值变为0,请选择合适树边,使得处理后,总耗时最小。

输出该最小值。

n<=300000,m<=300000

分析:

暴力60分易得,nmlogn,暴力枚举变权值边即可。

正解:

tarjanLCA + 二分答案 + 树上差分 + 贪心思想

0.离线预处理每个询问两点之间LCA,用tarjan,树剖会卡掉。从而处理出了任意两点之间距离。

1.发现单调性,二分这个最小答案,判断。

2.判断的时候,先找到所有dis值>mid的任务,它们肯定是要通过消掉一条公共边,使得合法的。

3.这个时候用贪心,设len为所有dis中最大值,合法的时候,贪心的要使去掉的公共边是所有公共边中最长的。len-k<=mid才合法。

4.所以要找到k。将所有这次的要处理的tot个任务,放在树上差分。dfs pushup即可。当一个点的权值=tot的时候,它与它父亲之间的边就是一条合法的公共边,所有公共边之间求最大值即可。

一切操作的正确性都易证明。

#include<bits/stdc++.h>
#define num ch-'0'
using namespace std;
const int N=300000+10;
const int inf=0x3f3f3f3f;
void read(int &x)
{
    x=0;char ch;
    while(!isdigit(ch=getchar()));
    for(x=num;isdigit(ch=getchar());x=x*10+num);
}
int n,m;
struct node{
    int nxt,to,val;
}bian[2*N],edge[2*N];
int hd[N],cnt;
int pre[N],cc;
bool vis[N];
int dis[N];
int w[N];
struct que{
    int x,y;
    int lca;
    int dist; 
}q[N];
int ans;
int L,R;
int mx,tot;
void add1(int x,int y,int z)//树边 
{
    bian[++cnt].nxt=hd[x];
    bian[cnt].to=y;
    bian[cnt].val=z;
    hd[x]=cnt;
}
void add2(int x,int y,int z)//询问边 
{
    edge[++cc].nxt=pre[x];
    edge[cc].to=y;
    edge[cc].val=z;
    pre[x]=cc;
}
int ff[N];
int fin(int x)//并查集 
{
    if(ff[x]==x) return x;
    return ff[x]=fin(ff[x]);
}
void tarjan(int x)//tarjan求 LCA 
{
    ff[x]=x;
    vis[x]=1;
    for(int i=pre[x];i;i=edge[i].nxt)
    {
        int y=edge[i].to;
        if(vis[y])
        {
            q[edge[i].val].lca=fin(y);
        }
    }
    for(int i=hd[x];i;i=bian[i].nxt)
    {
        int y=bian[i].to;
        if(!vis[y])
        {
            tarjan(y);
            ff[y]=x;
        }
    }
}
void dfs1(int x,int f,int d,int ds)//处理dis,到根节点距离 
{
    dis[x]=ds;
    for(int i=hd[x];i;i=bian[i].nxt)
    {
        int y=bian[i].to;
        if(y==f) continue;
        dfs1(y,x,d+1,ds+bian[i].val);
    }    
}
void dfs3(int x,int f,int df)//树上差分 
{
    for(int i=hd[x];i;i=bian[i].nxt)
    {
        int y=bian[i].to;
        if(y==f) continue;
        dfs3(y,x,bian[i].val);
        w[x]+=w[y];
    }
    if(w[x]==tot){
        mx=max(mx,df);
    }
}

bool binary(int x)//二分 
{    
    memset(w,0,sizeof w);
    int len=0;
    tot=0;
    mx=0;
    for(int i=1;i<=m;i++)
    {
        if(q[i].dist>x)
        {
            tot++;
            w[q[i].x]++,w[q[i].y]++,w[q[i].lca]-=2;    
            len=max(len,q[i].dist);
        }    
    }
    dfs3(1,-1,0);
    if(len-mx<=x) return true;
    else return false;
}
int main()
{
    read(n);read(m);
    int x,y,z;
    for(int i=1;i<=n-1;i++)
    {
        read(x);read(y);read(z);
        add1(x,y,z);
        add1(y,x,z);
    }
    dfs1(1,-1,1,0);
    
    for(int i=1;i<=m;i++)
    {
        read(x);read(y);
        q[i].x=x,q[i].y=y;
        add2(x,y,i),add2(y,x,i);
    }
       tarjan(1);
       
    for(int i=1;i<=m;i++)
    { 
    q[i].dist=dis[q[i].x]+dis[q[i].y]-2*dis[q[i].lca];
    R=max(R,q[i].dist);
    }
    R+=10;
    L=0;
    ans=inf;
    while(L<=R)//二分答案 
    {
        int mid=(L+R)/2;
        
        bool fll=binary(mid);
        
        if(fll) {
            ans=min(ans,mid);R=mid-1;
        }
        else{
            L=mid+1;
        }
    }
    printf("%d",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/Miracevin/p/9069936.html