zoukankan      html  css  js  c++  java
  • [NOIP2015] 运输计划(第二弹)

    题意:

    给你一棵n个结点的树,有m个运输计划,每个计划表示从一个点x到一个点y的路径长度,你可以将一条边的长度赋为0,问完成所有计划的最短时间。

    题解:

    再写一遍了;

    要你扣掉一条边,直接扣掉再计算答案至少要(O(nm))的复杂度,再加上这题常数比较大,最多50分吧;

    那么二分答案转化问题,二分完成所有任务的时间,若扣掉一条边后用时最长的任务的时间小于mid,则返回true;

    对于那些大于mid的任务,考虑扣掉一条边,应为我们二分了完成所有任务的时间,所以扣掉的这条边一定是要被所有的任务路径经过,并且越大越好,于是就要用到树上差分找到这条边,一遍dfs即可;

    复杂度(O((n+m)log1000))

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #define ll long long
    #define N 300010
    using namespace std;
    
    int n,m,L,e_num,mx,mw,num,ans=1<<30;
    int nxt[N<<1],to[N<<1],w[N<<1],h[N];
    int fa[N],dep[N],top[N],siz[N],son[N],dist[N],cnt[N];
    
    struct Node {int x,y,z;}task[N];
    
    inline int gi() {
      int x=0,o=1; char ch=getchar();
      while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
      if(ch=='-') o=-1,ch=getchar();
      while(ch>='0' && ch<='9') x=x*10+ch-'0',ch=getchar();
      return o*x;
    }
    
    inline void add(int x, int y, int z) {
      nxt[++e_num]=h[x],to[e_num]=y,w[e_num]=z,h[x]=e_num;
    }
    
    inline void dfs1(int u) {
      siz[u]=1;
      for(int i=h[u]; i; i=nxt[i]) {
        int v=to[i];
        if(v==fa[u]) continue;
        fa[v]=u,dep[v]=dep[u]+1,dist[v]=dist[u]+w[i];
        dfs1(v);
        if(siz[v]>siz[son[u]]) son[u]=v;
        siz[u]+=siz[v];
      }
    }
    
    inline void dfs2(int u) {
      if(son[u]) top[son[u]]=top[u],dfs2(son[u]);
      for(int i=h[u]; i; i=nxt[i]) {
        int v=to[i];
        if(v==fa[u] || v==son[u]) continue;
        top[v]=v,dfs2(v);
      }
    }
    
    inline void dfs3(int u) {
      for(int i=h[u]; i; i=nxt[i]) {
        int v=to[i];
        if(v==fa[u]) continue;
        dfs3(v);
        if(cnt[v]==num) mw=max(mw,w[i]);
        cnt[u]+=cnt[v];
      }
    }
    
    inline int lca(int x, int y) {
      while(top[x]!=top[y]) {
        if(dep[top[x]]>dep[top[y]]) x=fa[top[x]];
        else y=fa[top[y]];
      }
      if(dep[x]<dep[y]) return x;
      else return y;
    }
    
    inline bool check(int mid) {
      mx=mw=num=0;
      for(int i=1; i<=n; i++) cnt[i]=0;
      for(int i=1; i<=m; i++) {
        if(task[i].z>mid) {
          cnt[task[i].x]++,cnt[task[i].y]++,cnt[lca(task[i].x,task[i].y)]-=2;
          mx=max(mx,task[i].z),num++;
        }
      }
      dfs3(1);
      return mx-mw<=mid;
    }
    
    int main() {
      n=gi(),m=gi();
      for(int i=1; i<n; i++) {
        int x=gi(),y=gi(),z=gi();
        add(x,y,z),add(y,x,z);
      }
      fa[1]=1,dep[1]=1,top[1]=1;
      dfs1(1),dfs2(1);
      lca(4,5);
      for(int i=1; i<=m; i++) {
        int x=gi(),y=gi(),z=dist[x]+dist[y]-dist[lca(x,y)]*2;
        task[i]=(Node){x,y,z};
        L=max(L,z);
      }
      int r=L,l=r-1000,mid;
      while(l<=r) {
        mid=(l+r)>>1;
        if(check(mid)) ans=min(ans,mid),r=mid-1;
        else l=mid+1;
      }
      printf("%d", ans);
      return 0;
    }
    
  • 相关阅读:
    AtCoder Grand Contest 029C
    AtCoder Grand Contest 034D
    JZOJ 4418. 【HNOI2016模拟4.1】Prime的把妹计划(单调栈+线段树)
    COCI 2015/2016 PROKLETNIK(单调栈+线段树)
    常用数论定理(费马小定理&欧拉定理&扩展欧拉定理)
    JZOJ 3252. 【GDOI三校联考】炸弹(树形DP)
    prufer序列入门
    NOI2020全国统一省选-GDOI游记(爆炸记)
    Manacher算法操作详解
    JZOJ 6493. 【GDOI2020模拟03.04】迷宫(状压DP)
  • 原文地址:https://www.cnblogs.com/HLXZZ/p/7708363.html
Copyright © 2011-2022 走看看