bzoj 1999 分类: bzoj noip 2015-08-07 23:37 15人阅读 评论(0) 收藏
树网的核 加强版,好题~
可以证明,如果存在多条直径,选取任意一条直径都是等价的。
证明:
如图,考虑两条直径 AB, CD,其中它们的重合部分为 EF
A B
\ /
\ /
\_____________________/
/E F\
/ \
/ \
C D
先证 |AE| = |CE|, |BF| = |DF|
|AE| +|EF| + |BF| = |CE| + |EF| + |DF|
若|AE| > |CE|, 则 |BF| < |DF|,那么 |AD| >|AB|
而AB为树的直径,推出矛盾,所以 |AE| <= |CE|
同理可证 |AE| >= |CE|,|BF| <= |DF|,|BF| >= |DF|
现在证明,在任意一条直径上选取路径
如果
如果
- 当
p 在AB上时,CD上的点对偏心距的贡献为 |CE| 和 |DF| - 当
p 在CD上时,AB上的点对偏心距的贡献为 |AE| 和 |BF|
综上所述,结论成立。
根据贪心的原则,在直径上选取尽量长的路径
对于这条路径,如何计算偏心距?
记直径左端点为 L, 右端点为 R,这条路径左端点为
其中
对于
时间复杂度:
/**************************************************************
Problem: 1999
User: cyxhahaha
Language: C++
Result: Accepted
Time:1496 ms
Memory:58324 kb
****************************************************************/
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <ctime>
#include <string>
#include <map>
#include <vector>
#include <stack>
#include <queue>
#include <utility>
#include <iostream>
#include <algorithm>
template<class Num>void read(Num &x)
{
char c; int flag = 1;
while((c = getchar()) < '0' || c > '9')
if(c == '-') flag *= -1;
x = c - '0';
while((c = getchar()) >= '0' && c <= '9')
x = (x<<3) + (x<<1) + (c-'0');
x *= flag;
return;
}
template<class Num>void write(Num x)
{
if(x < 0) putchar('-'), x = -x;
static char s[20];int sl = 0;
while(x) s[sl++] = x%10 + '0',x /= 10;
if(!sl) {putchar('0');return;}
while(sl) putchar(s[--sl]);
}
#define REP(__x,__st,__ed) for(int __x = (__st); __x <= (__ed); __x++)
const int maxn = 5e5 + 5, Nya = -1;
int n, S;
struct Edge
{
int v, w, next;
Edge(int v=0,int w=0,int next=0):v(v),w(w),next(next){}
}edge[maxn<<1];
int el, head[maxn];
int dist[maxn], path[maxn], len;
int Lmax[maxn], Rmax[maxn], Mmax[maxn];
bool hash[maxn];
void NewEdge(int u,int v,int w)
{
edge[++el] = Edge(v, w, head[u]), head[u] = el;
}
void dfs(int a,int fa,int arr[])
{
for(int i = head[a]; i; i = edge[i].next)
{
int p = edge[i].v;
if(p == fa) continue;
arr[p] = arr[a] + edge[i].w;
dfs(p, a, arr);
}
}
int gainmax(int a,int fa)
{
int ret = 0;
for(int i = head[a]; i ; i = edge[i].next)
{
int p = edge[i].v;
if(p == fa || hash[p]) continue;
ret = std::max(gainmax(p, a) + edge[i].w, ret);
}
return ret;
}
bool find(int a,int aim,int fa)
{
if(a == aim)
{
path[++len] = a;
return true;
}
for(int i = head[a]; i; i = edge[i].next)
{
if(edge[i].v == fa) continue;
if(find(edge[i].v, aim, a))
{
path[++len] = a;
return true;
}
}
return false;
}
void init()
{
int u, v, w;
read(n), read(S);
for(int i = 1; i < n; i++)
{
read(u), read(v), read(w);
NewEdge(u, v, w);
NewEdge(v, u, w);
}
}
void diameter()
{
int s = 1, t = 1;
dist[1] = 0, dfs(1, 0, dist);
REP(i, 1, n)
if(dist[i] > dist[s])
s = i;
dist[s] = 0, dfs(s, 0, dist);
REP(i, 1, n)
if(dist[i] > dist[t])
t = i;
find(t, s, 0);
REP(i, 1, len) hash[path[i]] = true;
REP(i, 1, len) Mmax[i] = gainmax(path[i], 0);
REP(i, 1, len)
{
Lmax[i] = dist[path[i]] - dist[path[1]];
Rmax[i] = dist[path[len]] - dist[path[i]];
}
}
void solve()
{
static int line[maxn];
int f = 0, r = 0, ans = Nya, cal;
for(int i = 1, j = 1; i <= len; i++)
{
if(f != r && line[f] < i) line[f++] = 0;
while(j <= n && dist[path[j]] - dist[path[i]] <= S)
{
while(f != r && Mmax[line[r-1]] <= Mmax[j]) line[--r] = 0;
line[r++] = j++;
}
cal = std::max(Mmax[line[f]], std::max(Lmax[i], Rmax[j-1]));
if(ans == Nya || cal < ans) ans = cal;
}
write(ans);
}
int main()
{
#ifndef ONLINE_JUDGE
freopen("1999.in","r",stdin);
freopen("1999.out","w",stdout);
#endif
init(), diameter(), solve();
#ifndef ONLINE_JUDGE
fclose(stdin);
fclose(stdout);
#endif
return 0;
}
版权声明:本文为博主原创文章,未经博主允许不得转载。