UOJ 150 [NOIP2015]运输计划

来源:互联网 发布:怎么查淘宝单号码查询 编辑:程序博客网 时间:2024/06/05 04:35

二分+树链剖分求LCA+差分

要求最远的值最小,于是我们可以考虑二分答案。

对于每一个二分出来的lim,我们只需要判断所有路径(指运输计划里面所有的路径)中长度大于lim的所有路径是否存在一条公共边,使得最长路径减去它可以小于等于lim(如果最长的可以,那么短的也肯定可以)

那么怎么求这条边?我刚开始的想法是树剖+线段树,对每一条超过lim的路径上的边的都记+1,然后暴力枚举边来找公共边。但是这样是O(nlog3n) 不太科学。。。

实际上可以根本不线段树,维护一个树上差分数组f即可,值也是照样加。。。预处理所有LCA,复杂度就是O(nlogn)

#include<cstdio>#include<cstring>#include<algorithm>#define N 300005#define reg registerusing namespace std;struct edge{int next,to,val;}e[N<<1];int ecnt;int n, m, last[N], fa[N], dep[N], siz[N], top[N], son[N], u[N], v[N], d[N], dis[N], Lca[N], f[N], mxlen, mx, tot, b[N];int in(){    reg int r=0;    reg char c=getchar();    while(c<'0'||c>'9')c=getchar();    while(c>='0'&&c<='9')r=r*10+c-'0',c=getchar();    return r;}void addedge(int a, int b, int c){    e[++ecnt]=(edge){last[a],b,c};    last[a]=ecnt;}void dfs1(int x){    dep[x]=dep[fa[x]]+1;    siz[x]=1;    for(int i = last[x]; i; i=e[i].next)    {        int y=e[i].to;        if(y==fa[x])continue;        fa[y]=x;        dis[y]=dis[x]+e[i].val;        dfs1(y);        siz[x]+=siz[y];        if(siz[y]>siz[son[x]])            son[x]=y;    }}void dfs2(int x){    if(son[fa[x]]==x)top[x]=top[fa[x]];    else top[x]=x;    for(int i = last[x]; i; i=e[i].next)    {        int y=e[i].to;        if(y!=fa[x])            dfs2(y);    }}int lca(int a, int b){    while(top[a]!=top[b])    {        if(dep[top[a]]>dep[top[b]])            a=fa[top[a]];        else b=fa[top[b]];    }    return dep[a]>dep[b]?b:a;}void make(int i){    f[u[i]]++;    f[v[i]]++;    f[Lca[i]]-=2;}void find(int x){    b[x]=f[x];    for(int i = last[x]; i; i=e[i].next)    {        int y=e[i].to;        if(y==fa[x])continue;        find(y);        b[x]+=b[y];        if(b[y]==tot)            mx=max(mx,e[i].val);    }}bool check(int lim){    tot=0;    memset(f,0,sizeof(f));    for(int i = 1; i <= m; i++)        if(d[i]>lim)        {            ++tot;            make(i);        }    mx=0;    find(1);    return mxlen-mx<=lim;}int main(){    n=in(), m=in();    for(int i = 1; i < n; i++)    {        int a=in(), b=in(), t=in();        addedge(a,b,t);        addedge(b,a,t);    }     dfs1(1);    dfs2(1);    for(int i = 1; i <= m; i++)    {        u[i]=in(), v[i]=in();         d[i]=dis[u[i]]+dis[v[i]]-2*dis[Lca[i]=lca(u[i],v[i])];        mxlen=max(mxlen,d[i]);    }       int l = max(0,mxlen-1000), r = mxlen;    while(l<r)    {        int mid=(l+r)>>1;        if(check(mid))r=mid;        else l=mid+1;    }    printf("%d\n",l);}
1 0
原创粉丝点击