题目链接
设 T = ( V , E , W ) T=(V,E,W) T=(V,E,W)是一个无圈且连通的无向图(也称为无根树),每条边到有正整数的权,我们称 T T T为树网(treebetwork),其中 V V V, E E E分别表示结点与边的集合, W W W表示各边长度的集合,并设 T T T有 n n n个结点。
路径:树网中任何两结点 a a a, b b b都存在唯一的一条简单路径,用 d ( a , b ) d(a, b) d(a,b)表示以 a , b a, b a,b为端点的路径的长度,它是该路径上各边长度之和。我们称 d ( a , b ) d(a, b) d(a,b)为 a , b a, b a,b两结点间的距离。
D ( v , P ) = min { d ( v , u ) } D(v, P)=\min\{d(v, u)\} D(v,P)=min{d(v,u)}, u u u为路径 P P P上的结点。
树网的直径:树网中最长的路径成为树网的直径。对于给定的树网 T T T,直径不一定是唯一的,但可以证明:各直径的中点(不一定恰好是某个结点,可能在某条边的内部)是唯一的,我们称该点为树网的中心。
偏心距 E C C ( F ) \mathrm{ECC}(F) ECC(F):树网T中距路径F最远的结点到路径FF的距离,即
E C C ( F ) = max { d ( v , F ) , v ∈ V } \mathrm{ECC}(F)=\max\{d(v, F),v \in V\} ECC(F)=max{d(v,F),v∈V}
任务:对于给定的树网 T = ( V , E , W ) T=(V, E, W) T=(V,E,W)和非负整数 s s s,求一个路径 F F F,他是某直径上的一段路径(该路径两端均为树网中的结点),其长度不超过 s s s(可以等于 s s s),使偏心距 E C C ( F ) ECC(F) ECC(F)最小。我们称这个路径为树网 T = ( V , E , W ) T=(V, E, W) T=(V,E,W)的核(Core)。必要时, F F F可以退化为某个结点。一般来说,在上述定义下,核不一定只有一个,但最小偏心距是唯一的。
下面的图给出了树网的一个实例。图中, A − B A-B A−B与 A − C A-C A−C是两条直径,长度均为 20 20 20。点 W W W是树网的中心, E F EF EF边的长度为 5 5 5。如果指定 s = 11 s=11 s=11,则树网的核为路径 D E F G DEFG DEFG(也可以取为路径 D E F DEF DEF),偏心距为 8 8 8。如果指定 s = 0 s=0 s=0(或 s = 1 s=1 s=1、 s = 2 s=2 s=2),则树网的核为结点 F F F,偏心距为 12 12 12。
共 n n n行。
第 1 1 1行,两个正整数 n n n和 s s s,中间用一个空格隔开。其中 n n n为树网结点的个数, s s s为树网的核的长度的上界。设结点编号以此为 1 , 2 , … , n 1,2,…,n 1,2,…,n。
从第 2 2 2行到第 n n n行,每行给出 3 3 3个用空格隔开的正整数,依次表示每一条边的两个端点编号和长度。例如,“ 247 2 4 7 247”表示连接结点 2 2 2与 4 4 4的边的长度为 7 7 7。
一个非负整数,为指定意义下的最小偏心距。
5 2
1 2 5
2 3 2
2 4 4
2 5 3
5
8 6
1 3 2
2 3 2
3 4 6
4 5 3
4 6 4
4 7 2
7 8 3
5
40 % 40\% 40%的数据满足: 5 ≤ n ≤ 15 5 \le n \le 15 5≤n≤15
70 % 70\% 70%的数据满足: 5 ≤ n ≤ 80 5 \le n \le 80 5≤n≤80
100 % 100\% 100%的数据满足: 5 ≤ n ≤ 300 , 0 ≤ s ≤ 1000 5 \le n \le 300,0 \le s \le 1000 5≤n≤300,0≤s≤1000。边长度为不超过 1000 1000 1000的正整数
bzoj上数据增强:
对于 70 % 70\% 70%的数据, n ≤ 200000 n\le200000 n≤200000
对于 100 % 100\% 100%的数据: n ≤ 500000 , s < 2 31 n\le500000, s<2^{31} n≤500000,s<231, 所有权值 < 500 <500 <500
NOIP 2007 提高第四题
设直径上的节点为 u 1 , u 2 , . . . , u t u_1,u_2,...,u_t u1,u2,...,ut,先把这几个节点标记为“已访问”,然后通过深度优先遍历,求出 d [ u i ] d[u_i] d[ui],表示从 u i u_i ui出发,不经过直径上的其他节点,能够到达的最远点的距离。
以 u i , u j ( i ≤ j ) u_i,u_j(i\le j) ui,uj(i≤j)为端点的树网的核的偏心距就是:
max ( max i ≤ k ≤ j { d [ u k ] } , d i s t ( u 1 , u i ) , d i s t ( u j , u t ) ) \max\Big(\max _{i\le k\le j}\{d[u_k]\},dist(u_1,u_i),dist(u_j,u_t)\Big) max(i≤k≤jmax{d[uk]},dist(u1,ui),dist(uj,ut))
上式可由直径的最长性简化为:
max ( max 1 ≤ k ≤ t { d [ u k ] } , d i s t ( u 1 , u i ) , d i s t ( u j , u t ) ) \max\Big(\max_{1\le k\le t}\{d[u_k]\},dist(u_1,u_i),dist(u_j,u_t)\Big) max(1≤k≤tmax{d[uk]},dist(u1,ui),dist(uj,ut))
max 1 ≤ k ≤ t { d [ u k ] } \max_{1\le k\le t}\{d[u_k]\} max1≤k≤t{d[uk]}对于 u i , u j u_i,u_j ui,uj来说是一个定值。只需枚举每个 u i u_i ui,同时用一个指针,在距离不超过s的前提下,每次沿着直径向后移动,得到 u j u_j uj更新答案。时间复杂度 O ( n ) O(n) O(n)
(详见李煜东《算法竞赛进阶指南》第364~365页)
#include
#include
#include
using namespace std;
const int N=5e5+10;
struct Edge{
int v,nx,w;
}e[N<<1];
int n,s,hd[N],tot,fa[N],vis[N],dis[N];
void addedge(int u,int v,int w)
{
e[tot].v=v;
e[tot].w=w;
e[tot].nx=hd[u];
hd[u]=tot++;
}
void dfs(int u,int f)
{
fa[u]=f;
for(int i=hd[u];~i;i=e[i].nx)
{
int v=e[i].v;
if(vis[v]||v==f)continue;
dis[v]=dis[u]+e[i].w;
dfs(v,u);
}
}
int main()
{
//freopen("in.txt","r",stdin);
memset(hd,-1,sizeof(hd));
scanf("%d%d",&n,&s);
int u,v,w;
for(int i=1;idis[r])r=i;
l=r;
dis[r]=0;
dfs(r,0);
for(int i=1;i<=n;i++)if(dis[i]>dis[l])l=i;
int i=l,j=l;
int ans=0x7fffffff;
for(;i;i=fa[i])
{
while(fa[j]&&dis[i]-dis[fa[j]]<=s)j=fa[j];
ans=min(ans,max(dis[j],dis[l]-dis[i]));
}
for(i=l;i;i=fa[i])vis[i]=1;
for(i=l;i;i=fa[i])dis[i]=0,dfs(i,fa[i]);
for(i=1;i<=n;i++)ans=max(ans,dis[i]);
printf("%d\n",ans);
return 0;
}
此题作为书上例题,难度有以下几点:
1.读懂题目。里面出现了不少定义和公式,大段信息中隐藏着一些结论。
2.分析性质,得出较为优化的解法。
3.代码实现,对于“沿着直径向后移动”,之前没想到怎么实现,最后发现其实可以数组实现,说明自己代码实现能力不足。