noip 2015 运输计划 (lca+二分)

/*
95 最后一个点T了 qian lv ji qiong 了
没学过树剖 听chx听xzc说的神奇的方法  Orz
首先求出每个计划的路径长度 这里写的倍增
然后二分答案
对于每个ans 统计>他的路径条数 tot 并维护最大差值 dec
并且对于每条不合法的路径维护每个点的经过次数
然后枚举点 如果经过次数==tot说明每一条不合法的都经过他
然后尝试把它建成虫洞 如果他对应边的权值>=dec 那么我们删掉它ans就合法了
关键是统计每个点在非法路径中的经过次数 :
维护sum数组 对于每个非法的路径起点a b LCA(a,b)==s sum[a]++ sum[b]++ sum[s]-=2
这样网上更新的话 经过的点的sum值都变成1 祖先s的变成0
这样就实现了sum数组的维护
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#define maxn 300100
using namespace std;
int n,m,num,head[maxn],ans,inf;
int fa[maxn][30],dep[maxn],dis[maxn],sum[maxn],edge[maxn];
struct node
{
    int u,v,t,pre;
}e[maxn*2];
struct Ans
{
    int ai,bi,anc,di;
}lca[maxn];
int init()
{
    int x=0;char s=getchar();
    while(s<‘0‘||s>‘9‘)s=getchar();
    while(s>=‘0‘&&s<=‘9‘){x=x*10+s-‘0‘;s=getchar();}
    return x;
}
void Add(int from,int to,int dis)
{
    num++;
    e[num].u=from;
    e[num].v=to;
    e[num].t=dis;
    e[num].pre=head[from];
    head[from]=num;
}
void Dfs(int now,int from,int c,int Dis)
{
    fa[now][0]=from;
    dep[now]=c;dis[now]=Dis;
    for(int i=head[now];i;i=e[i].pre)
      if(e[i].v!=from)
        {
          edge[e[i].v]=i;
          Dfs(e[i].v,now,c+1,Dis+e[i].t);
        }
}
void Get_fa()
{
    for(int j=1;j<=16;j++)
      for(int i=1;i<=n;i++)
        fa[i][j]=fa[fa[i][j-1]][j-1];
}
int Get_same(int a,int t)
{
    for(int i=0;i<16;i++)
     if(t&(1<<i)) a=fa[a][i];
    return a;
}
int LCA(int a,int b)
{
    if(dep[a]<dep[b])swap(a,b);
    a=Get_same(a,dep[a]-dep[b]);
    if(a==b)return a;
    for(int i=16;i>=0;i--)
      if(fa[a][i]!=fa[b][i])
        {
          a=fa[a][i];
          b=fa[b][i];
        }
    return fa[a][0];
}
void Init()
{
    n=init();m=init();
    int u,v,t;
    for(int i=1;i<=n-1;i++)
      {
          u=init();v=init();t=init();
          Add(u,v,t);Add(v,u,t);
      }
    Dfs(1,1,0,0);
    Get_fa();
    for(int i=1;i<=m;i++)
      {
          lca[i].ai=init();lca[i].bi=init();
          lca[i].anc=LCA(lca[i].ai,lca[i].bi);
          lca[i].di=dis[lca[i].ai]+dis[lca[i].bi]-2*dis[lca[i].anc];
          inf=max(inf,lca[i].di);
      }
}
void Up_sum(int now,int from)
{
    for(int i=head[now];i;i=e[i].pre)
      if(e[i].v!=from)
        {
          Up_sum(e[i].v,now);
          sum[now]+=sum[e[i].v];
        }
}
int Judge(int x)
{
    memset(sum,0,sizeof(sum));
    int tot=0,dec=0;
    for(int i=1;i<=m;i++)
      if(lca[i].di>x)//非法路径
        {
          tot++;
          dec=max(dec,lca[i].di-x);//最长非法路径与ans差值
          sum[lca[i].ai]++;
          sum[lca[i].bi]++;
          sum[lca[i].anc]-=2;
        }
    Up_sum(1,1);//更新sum数组
    for(int i=1;i<=n;i++)
      if(tot==sum[i]&&e[edge[i]].t>=dec)//删掉edge[i]这条边之后答案合法了
        return 1;
    return 0;
}
void Solve()//二分答案
{
    int l=0,r=inf;
    while(l<=r)
      {
          int mid=(l+r)/2;
          int tmp=Judge(mid);
          if(tmp==1)
            {
                r=mid-1;
                ans=mid;
          }
        else l=mid+1;
      }
}
void Printf()
{
    printf("%d\n",ans);
}
int main()
{
    //freopen("transport.in","r",stdin);
    //freopen("transport.out","w",stdout);
    Init();
    Solve();
    Printf();
    return 0;
}
时间: 2024-08-10 20:27:21

noip 2015 运输计划 (lca+二分)的相关文章

[noip 2015]运输计划 [LCA][树链剖分]

用了luogu上的题目描述 题目背景 公元 2044 年,人类进入了宇宙纪元. 题目描述 L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n-1 条航道连通了 L 国的所有星球. 小 P 掌管一家物流公司,该公司有很多个运输计划,每个运输计划形如:有一艘物 流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去.显然,飞船驶过一条航道 是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之 间不会产生任何干扰. 为了鼓励科技创

NOIP[2015] 运输计划(codevs 4632)

题目描述 Description 公元 2044 年,人类进入了宇宙纪元.L 国有 n 个星球,还有 n−1 条双向航道,每条航道建立在两个星球之间,这 n−1 条航道连通了 L 国的所有星球.小 P 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去.显然,飞船驶过一条航道是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之间不会产生任何干扰.为了鼓励科技创新, L 国国王同意小

NOIP 2015运输计划

题目背景 公元 2044 年,人类进入了宇宙纪元. 题目描述 L 国有 n 个星球,还有 n-1 条双向航道,每条航道建立在两个星球之间,这 n-1 条航道连通了 L 国的所有星球. 小 P 掌管一家物流公司,该公司有很多个运输计划,每个运输计划形如:有一艘物 流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去.显然,飞船驶过一条航道 是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之 间不会产生任何干扰. 为了鼓励科技创新,L 国国王同意小 P 的

NOIp 2015 运输计划

运输计划 问题大意 有n个星球与n-1条双向边,每条边有时间ti,有m个从vi到ui的运输计划.允许你将一条边的时间降为0.同时开始所有的计划,问最小要多少时间完成计划. 输入输出格式 输入格式: 输入文件名为 transport.in. 第一行包括两个正整数 n.m,表示 L 国中星球的数量及小 P 公司预接的运输计划的数量,星球从 1 到 n 编号. 接下来 n-1 行描述航道的建设情况,其中第 i 行包含三个整数 ai, bi 和 ti,表示第 i 条双向航道修建在 ai 与 bi 两个星

BZOJ 4326:NOIP2015 运输计划(二分+差分+lca)

NOIP2015 运输计划Description公元 2044 年,人类进入了宇宙纪元.L 国有 n 个星球,还有 n−1 条双向航道,每条航道建立在两个星球之间,这 n−1 条航道连通了 L 国的所有星球.小 P 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去.显然,飞船驶过一条航道是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之间不会产生任何干扰.为了鼓励科技创新, L

NOIP2015 运输计划(二分+LCA+差分)

4326: NOIP2015 运输计划 Time Limit: 30 Sec  Memory Limit: 128 MBSubmit: 308  Solved: 208[Submit][Status][Discuss] Description 公元 2044 年,人类进入了宇宙纪元.L 国有 n 个星球,还有 n−1 条双向航道,每条航道建立在两个星球之间,这 n−1 条航道连通了 L 国的所有星球.小 P 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 ui

NOIp2015 运输计划 [LCA] [树上差分] [二分答案]

我太懒了 吃掉了题面 题解 & 吐槽 一道很好的树上差分练习题. 不加fread勉强a过bzoj和luogu的数据,加了fread才能在uoj里卡过去. 可以发现,答案则是运输计划里花费的最大值,最大值最小,便是二分答案的标志. 那么该怎么check呢... 我们得找出所有超过限制的计划,这个过程可以在LCA倍增的过程中预处理出来. 然后再找出一些被这些计划都覆盖的边,找到最大的那条边,如果最大的计划花费减去最大的那条边小于x,那么x就是可行的. 但是该怎么找到那些被计划都覆盖的边呢... 我们

NOIP 2015 BZOJ 4326 运输计划 (树链剖分+二分)

题目大意:给一棵带有边权的树,有m条路径,可以使一条边的权值为0,求最远路径的最小值.题解:因为题目的数据点给的很明确 因此可以打n*n的去骗前五十分.另外m=1时可以特判另外打个程序骗60分.60分程序: #include<cstdio> #include<algorithm> int dep[100001],fa[100001],last[200001],next[200001],e[200001],val[200001],cost[100001],tot,a,b,vv,u[1

[NOIP2015]运输计划 D2 T3 LCA+二分答案+差分数组

[NOIP2015]运输计划 D2 T3 Description 公元2044年,人类进入了宇宙纪元. L国有n个星球,还有n-1条双向航道,每条航道建立在两个星球之间,这n-1条航道连通了L国的所有星球. 小P掌管一家物流公司,该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从ui号星球沿最快的宇航路径飞行到vi号星球去.显然,飞船驶过一条航道是需要时间的,对于航道j,任意飞船驶过它所花费的时间为tj,并且任意两艘飞船之间不会产生任何干扰. 为了鼓励科技创新,L国国王同意小P的物流