[Noip2007]Core树网的核

嘟嘟嘟

首先求树的直径两次bfs即可,实际上bfs就是最短路,因为树上路径是唯一的,所以用任何一种遍历方法都行(spfa和dijkstra当然也可以)。

可以证明,只要求出任意一条直径就行了,为什么呢?考虑一下,如果我们在直径上选了一段,那么最远偏心距可肯定是到直径两端的最大值,和直径外的点无关,只和直径的长度有关。

于是我们求完了直径。然后在直径上搞一搞:很容易想到,如果当前选了一段长度为a,他还可以延伸为b,且a < b < s,那么b的答案一定比a优。因此我们建立一个双指针L,R,代表当前选取的一段的左右端点,当L一定时,R要尽量远,如果超出了s,L指针就向前挪一个节点。其中dis数组就充当了前缀和数组的作用,然后每一次都尝试用max(dis[L], dis[end] - dis[R])更新答案,时间复杂度为O(n)。

需要注意的是,因为我们存路径都是反向存的,因此上面实际上应该是max(dis[R], dis[end] - dis[L]),而且跳的时候不是L++,R++,而是L = pre[L], R = pre[R].

别忘了还有一种情况:就是直径长度小于s,此时的答案应该是不在直径上的点到直径的最大值,只要对于直径上每一个节点向外dfs找就行,因为每一个节点只会遍历一次,所以时间复杂度还是O(n)。

  1 #include<cstdio>
  2 #include<iostream>
  3 #include<cmath>
  4 #include<algorithm>
  5 #include<cstring>
  6 #include<cstdlib>
  7 #include<cctype>
  8 #include<vector>
  9 #include<stack>
 10 #include<queue>
 11 using namespace std;
 12 #define enter puts("") 
 13 #define space putchar(' ')
 14 #define Mem(a) memset(a, 0, sizeof(a))
 15 #define rg register 
 16 typedef long long ll;
 17 typedef double db;
 18 const int INF = 0x3f3f3f3f;
 19 const db eps = 1e-8;
 20 const int maxn = 5e5 + 5;
 21 inline ll read()
 22 {
 23     ll ans = 0;
 24     char ch = getchar(), last = ' ';
 25     while(!isdigit(ch)) {last = ch; ch = getchar();}
 26     while(isdigit(ch)) {ans = ans * 10 + ch - '0'; ch = getchar();}
 27     if(last == '-') ans = -ans;
 28     return ans;
 29 }
 30 inline void write(ll x)
 31 {
 32     if(x < 0) x = -x, putchar('-');
 33     if(x >= 10) write(x / 10);
 34     putchar(x % 10 + '0');
 35 }
 36 
 37 int n, s;
 38 vector<int> v[maxn], c[maxn];
 39 
 40 bool vis[maxn];
 41 int dis[maxn], pre[maxn], w[maxn];
 42 int bfs(int s)
 43 {
 44     Mem(vis); Mem(pre); Mem(w); Mem(dis);
 45     queue<int> q; q.push(s);
 46     vis[s] = 1;
 47     dis[s] = 0;
 48     while(!q.empty())
 49     {
 50         int now = q.front(); q.pop();
 51         for(int i = 0; i < (int)v[now].size(); ++i)
 52         {
 53             if(!vis[v[now][i]])
 54             {
 55                 vis[v[now][i]] = 1;
 56                 dis[v[now][i]] = dis[now] + c[now][i];
 57                 pre[v[now][i]] = now;
 58                 w[v[now][i]] = c[now][i];
 59                 q.push(v[now][i]);
 60             }
 61         }
 62     }
 63     int Max = -1, pos;
 64     for(int i = 1; i <= n; ++i) if(dis[i] > Max) Max = dis[i], pos = i;
 65     return pos;
 66 }
 67 
 68 int ans = INF;
 69 
 70 int dfs(int now, int x)
 71 {
 72     int ret = -1;
 73     for(int i = 0; i < (int)v[now].size(); ++i)
 74     {
 75         if(!vis[v[now][i]])
 76         {
 77             vis[v[now][i]] = 1;
 78             ret = max(ret, dfs(v[now][i], x + c[now][i]));
 79         }
 80     }
 81     return max(ret, x);
 82 }
 83 void solve(int b, int a)
 84 {
 85     Mem(vis);
 86     ans = -1;
 87     for(int i = b; i; i = pre[i]) {vis[pre[i]] = vis[i] = 1; ans = max(ans, dfs(i, 0));}
 88     write(ans); enter;
 89 }
 90 
 91 int main()
 92 {
 93     n = read(); s = read();
 94     for(int i = 1; i < n; ++i)
 95     {
 96         int x = read(), y = read(), co = read();
 97         v[x].push_back(y); c[x].push_back(co);
 98         v[y].push_back(x); c[y].push_back(co);
 99     }
100     int a = bfs(1);
101     int b = bfs(a);
102     if(s >= dis[b]) {solve(b, a); return 0;}        
103     int L = b, R = b;
104     for(int i = b; i; i = pre[i])
105     {
106         while(L != R && dis[L] - dis[R] + w[i] > s) L = pre[L];
107         if(dis[L] - dis[R] + w[i] > s) L = pre[i];
108         R = pre[i];
109         ans = min(ans, max(dis[R], dis[b] - dis[L]));
110     }
111     write(ans); enter;
112     return 0;
113 }
View Code
原文地址:https://www.cnblogs.com/mrclr/p/9617934.html