NOIP2015:运输计划

花了两天终于A掉了这个题

从这个题里学会了树上差分/前缀和这个神奇的东西

觉得在树上弄区间问题树上差分和树上前缀和很有用

代码如下(被洛谷卡常T了一个点,然后把二分的max调小,卡过去了)

#include <stdio.h>
#include <string.h>
#include <algorithm>
#define MAXN 310000
using namespace std; 

struct edge {
  int e, v, next; 
} E[2*MAXN]; 
int e_cnt; 

int s[MAXN], e[MAXN], r[MAXN], len[MAXN]; 
int f[MAXN][20]; 
int fa[MAXN]; 
int dep[MAXN]; 
int N, M; 
int G[MAXN]; 
int sum[MAXN]; 
int cnt[MAXN]; 
int lim, maxdist; 

#define add(ss, ee, vv) do {            \
    E[++e_cnt].e = ee; E[e_cnt].v = vv;        \
    E[e_cnt].next = G[ss];            \
    G[ss] = e_cnt;                \
  } while(0)
void build(int now, int deep) {
  dep[now] = deep; 
  for(int i = G[now]; i != 0; i = E[i].next) {
    if(E[i].e != fa[now]) {
      fa[E[i].e] = now; 
      sum[E[i].e] = sum[now]+E[i].v; 
      f[E[i].e][1] = now; 
      build(E[i].e, deep+1); 
    }
  }
}
void LCA_init() {
  for(int k = 2; k <= 20; k++)
    for(int i = 1; i <= N; i++)
      f[i][k] = f[f[i][k-1]][k-1]; 
}
int LCA(int aa, int bb) {
  register int i, a = aa, b = bb; 
  if(dep[a] < dep[b]) swap(a, b); 
  for(i = 20; dep[a] > dep[b]; i--) if(f[a][i] && dep[f[a][i]] >= dep[b]) a = f[a][i]; 
  if(a == b) return a; 
  for(i = 20; i >= 0; i--)
    if(f[a][i] != f[b][i]) {
      a = f[a][i]; 
      b = f[b][i]; 
    }
  return f[a][1]; 
}
void dfs(int now) {
  for(int i = G[now]; i != 0; i = E[i].next) {
    if(E[i].e != fa[now]) {
      dfs(E[i].e); 
      cnt[now] += cnt[E[i].e]; 
    }
  }
}

bool judge() {
  int c = 0, maxedge = 0, i; 
  memset(cnt, 0, sizeof(cnt)); 
  for(i = 1; i <= M; i++) 
    if(len[i] > lim) 
      c++, cnt[s[i]]++, cnt[e[i]]++, cnt[r[i]] -= 2;  
  dfs(1); 
  for(i = 1; i <= N; i++)
    if(cnt[i] >= c) 
      maxedge = max(maxedge, sum[i]-sum[fa[i]]); 
  if(maxdist > lim+maxedge) return false; 
  return true; 
}

int main() {
  int i, j; 
  int ss, ee, vv; 
  scanf("%d%d", &N, &M); 
  for(i = 1; i <= N-1; i++) {
    scanf("%d%d%d", &ss, &ee, &vv); 
    add(ss, ee, vv); add(ee, ss, vv); 
  }
  build(1, 1); 
  LCA_init(); 
  for(i = 1; i <= M; i++) {
    scanf("%d%d", &s[i], &e[i]); 
    r[i] = LCA(s[i], e[i]); 
    len[i] = sum[s[i]]+sum[e[i]]-2*sum[r[i]]; 
    maxdist = max(maxdist, len[i]); 
  }
  int start = 0, mid, end = 157654321; 
  while(start < end-1) {
    lim = mid = (start+end)/2; 
    if(judge() == false) start = mid+1; 
    else end = mid; 
  }
  lim = start; 
  if(judge() == false) printf("%d\n", end); 
  else printf("%d\n", start); 
  return 0; 
}

 

posted @ 2017-10-12 16:37  全宇宙最最最菜的菜鸡  阅读(184)  评论(0编辑  收藏  举报