Leo_h

2333

【NOIP2015】【bzoj4326】【uoj146】运输计划 ——O(n*log)算法

最大数据是n,m=300000,所以应该试着把时间压在n*log(xxx)以内 //在本算法下,xxx=n*∑ti

考虑修改一条边后,所有长度大于答案的路径都被缩短,因此二分答案

设二分到的答案为k

那么被删掉的边一定是所有长于k的路径的公共边之一,因此问题转化为,在O(m)时间内求出m条路径的交集中的最长边

首先,可以在O(1)时间内求出两条路径的交集:对于路径s-t 和 s'-t' 分别求出s'到s-t上最近点u,t'到s-t上最近点v,那么交集就是(u,v)

这里若无解,交集会变成s-t上一个点,结束时特判即可。在其他题目中求路径交集需要再反向求一次,在此不详细解释。

求最近点代码如下

int closest(int x,int s,int t)
{
    int r=lca(s,t);
    if(lca(x,r)!=r) return r;
    int p=lca(x,s);
    return p==r?lca(x,t):p;
}

大致思路是,若最近点不是s和t 的公共祖先,那么判断是s那侧还是t那侧

其中lca要转化为RMQ,方可O(1)实现

找到交集后,从两个顶点开始逐条边向父节点移动,直到撞上就可以了,效率很低,O(n)但不会影响整体复杂度


代码:

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
#define maxm 300005
#define maxn 300005
#define maxt 600005
#define lca(u,v) rmq(pre[u],pre[v])
#define mid (l+r)/2
int n,m;
struct edge
{
    int v;
    int w;
    int next;
    edge(int v,int w,int next):v(v),w(w),next(next){}
    edge(){}
}e[maxn*2];
int newedge,dfsclock;
int ind[maxn];
void addedge(int u,int v,int l)
{
    e[++newedge]=edge(v,l,ind[u]);
    ind[u]=newedge;
}
int f[maxn];
int pre[maxn];
int dis[maxn],dep[maxn];
int visiting[maxt];
int st[maxt][20];
int log[maxt];
void dfs(int u,int fa)
{
    f[u]=fa;
    dep[u]=dep[fa]+1;
    pre[u]=++dfsclock;
    visiting[dfsclock]=u;
    for(int i=ind[u];i;i=e[i].next)
    {
        int v=e[i].v;
        if(v!=fa) 
        {
            dis[v]=dis[u]+e[i].w;
            dfs(v,u);
            visiting[++dfsclock]=u;
        }
    }
}
void rmq_init()
{
    log[1]=0;
    for(int i=2;i<=dfsclock;i++) log[i]=log[i>>1]+1;
    dep[0]=0x7fffffff;
    for(int i=1;i<=dfsclock;i++)
        st[i][0]=visiting[i];
    for(int l=1,t=0;l<=dfsclock;l<<=1,t++)
        for(int i=1;i+l<=dfsclock;i++)
            st[i][t+1]=dep[st[i][t]]<dep[st[i+l][t]]?st[i][t]:st[i+l][t];
}
int rmq(int l,int r)
{
    if(l>r) swap(l,r);
    int k=log[r-l+1];
    int a=st[l][k],b=st[r-(1<<k)+1][k];
    return dep[a]<dep[b]?a:b;
}
int closest(int x,int s,int t)
{
    int r=lca(s,t);
    if(lca(x,r)!=r) return r;
    int p=lca(x,s);
    return p==r?lca(x,t):p;
}
struct mission
{
    int s,t;
    int l;
    void input()
    {
        scanf("%d%d",&s,&t);
        l=dis[s]+dis[t]-2*dis[lca(s,t)];
    }
}q[maxm];
int ln[maxm];
bool operator <(mission a,mission b)
{
    return a.l<b.l;
}
bool judge(int x)
{
    int p=upper_bound(ln+1,ln+m+1,x)-ln;
    if(p==m+1) return true;
    int ss=q[p].s,tt=q[p].t;
    int maxlen=0;
    for(int i=p+1;i<=m;i++)
    {
        ss=closest(q[i].s,q[i].t,ss);
        tt=closest(q[i].s,q[i].t,tt);
    }
    if(ss==tt) return false;
    while(ss!=tt)
    {
        int &a=dep[ss]>dep[tt]?ss:tt;
        maxlen=max(maxlen,dis[a]-dis[f[a]]);
        a=f[a];
    }
    if(ln[m]-maxlen<=x) return true;
    else return false;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<n;i++)
    {
        int a,b,t;
        scanf("%d%d%d",&a,&b,&t);
        addedge(a,b,t);
        addedge(b,a,t);
    }
    dfs(1,0);
    rmq_init();
    for(int i=1;i<=m;i++) q[i].input();
    sort(q+1,q+m+1);
    for(int i=1;i<=m;i++) ln[i]=q[i].l;
    int l=0,r=0x7fffffff;
    while(l<r)
    {
        if(judge(mid)) r=mid;
        else l=mid+1;
    }
    printf("%d",l);
}





阅读更多
版权声明:本文为博主原创文章,转载请注明源网址blog.csdn.net/leo_h1104 https://blog.csdn.net/Leo_h1104/article/details/53085214
文章标签: OI 算法 bzoj
个人分类: OI BZOJ LCA RMQ
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

【NOIP2015】【bzoj4326】【uoj146】运输计划 ——O(n*log)算法

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭