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; }