BZOJ-4326: NOIP2015 运输计划 二分+树上差分

[文章目录]

Description

公元 2044 年,人类进入了宇宙纪元。L 国有 n 个星球,还有 n−1 条双向航道,每条航道建立在两个星球之间,这 n−1 条航道连通了 L 国的所有星球。小 P 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去。显然,飞船驶过一条航道是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之间不会产生任何干扰。为了鼓励科技创新, L 国国王同意小 P 的物流公司参与 L 国的航道建设,即允许小P 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。在虫洞的建设完成前小 P 的物流公司就预接了 m 个运输计划。在虫洞建设完成后,这 m 个运输计划会同时开始,所有飞船一起出发。当这 m 个运输计划都完成时,小 P 的物流公司的阶段性工作就完成了。如果小 P 可以自由选择将哪一条航道改造成虫洞, 试求出小 P 的物流公司完成阶段性工作所需要的最短时间是多少?(n,m<=300'000)

我,,,蒟蒻啊。。。

看到数据就想到应该是n*log(n)或是m的。。。然后就各种想,并没有想到二分答案。所以说还是。。。需要加油啊

那个将询问sort的过程可以直接改成O(m)扫,会快很多。不改的话,虽然理论上少扫了几个点,但是mlog(m)的排序还是很浪费时间的。

想法就是先将每个询问的链的长度搞出来,然后二分答案,对于每一个答案,把询问中链长比二分答案大的链在树上差分,以便在O(n)的时间将每条边都被那几条链通过了弄出来。然后在找一条边使得每条链都经过,并且使得每条链的长度都小于二分出的答案。

二分答案???我为什么没有想到。。。天啊。。。啊啊啊啊啊

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define N 310000
int n,m,now,mxx,head[N],nxt[N<<1],to[N<<1],data[N<<1],cnt,k1,k2,deep[N],fa[N][20],mx,dis[N],num[N],ti[N],goal,ans,target;
int read()
{
    int re=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9')
        re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
    return re;
}
struct node
{
    int v1,v2,anc,data;
}a[N];
void add(int x,int y,int z)
{
    to[++cnt]=y;
    data[cnt]=z;
    nxt[cnt]=head[x];
    head[x]=cnt;
}
void dfs(int x)
{
    int pre=fa[x][0];
    deep[x]=deep[pre]+1;
    for(int y,i=head[x];i;i=nxt[i])
    {
        y=to[i];
        if(y!=pre) fa[y][0]=x,dis[y]=dis[x]+data[i],dfs(y);
    }
}
int lca(int x,int y)
{
    if(deep[x]<deep[y]) swap(x,y);
    int tmp=deep[x]-deep[y];
    for(int i=0;(1<<i)<=tmp;i++)
        if(tmp&(1<<i)) x=fa[x][i];
    if(x==y)return x;
    for(int i=mx;i>=0;i--)
        if(fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
    return fa[x][0];
}
bool cmp(node x,node y)
{
    return x.data<y.data;
}
void dfs_num(int x)
{
    int pre=fa[x][0];
    for(int y,i=head[x];i;i=nxt[i])
    {
        y=to[i];
        if(y!=pre) dfs_num(y), ti[x]+=ti[y];
    }
    ti[x]+=num[x];
    if(ti[x]==goal) target=max(target,dis[x]-dis[fa[x][0]]);
}
bool check(int x,int tar)
{
    if(x<now)
        for(int i=x;i<now;i++)
            num[a[i].v1]++,num[a[i].v2]++,num[a[i].anc]-=2;
    else 
        for(int i=now;i<x;i++) 
            num[a[i].v1]--,num[a[i].v2]--,num[a[i].anc]+=2;
    now=x;
    goal=m-x+1;
    memset(ti,0,sizeof(ti));
    target=0;
    dfs_num(1);
    return mxx-target<=tar;
}
bool ok(int x)
{
    int mid,l=1,r=m+1,re;
    while(l<r)
    {
        mid=l+r>>1;
        if(a[mid].data>x) re=mid,r=mid;
        else l=mid+1;
    }
    return check(re,x);
}
int main()
{
    n=read();m=read(); now=m+1;
    for(int x,y,z,i=1;i<n;i++)
    {
        x=read();y=read();z=read();
        add(x,y,z);add(y,x,z);
    }
    deep[1]=1;dfs(1);
    for(;(1<<mx)<=n;mx++);
    for(int i=1;i<=mx;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++)
    {
        a[i].v1=read(),a[i].v2=read();
        a[i].anc=lca(a[i].v1,a[i].v2);
        a[i].data=dis[a[i].v1]+dis[a[i].v2]-(dis[a[i].anc]<<1);
    }
    sort(a+1,a+m+1,cmp);
    a[0].data=1<<30;
    //for(int i=1;i<=m;i++)
      //  printf("%d: v %d v %d anc %d dis %d\n",i,a[i].v1,a[i].v2,a[i].anc,a[i].data);
    mxx=a[m].data;
    int l=0,r=mxx+1;
    while(l<r)
    {
        int mid=l+r>>1;
        if(ok(mid)) ans=mid,r=mid;
        else l=mid+1;
    }
    printf("%d",ans);
    return 0;
}

 

发表评论

邮箱地址不会被公开。 必填项已用*标注