首页 > 代码库 > 【DFS好题】BZOJ1999- [Noip2007]Core树网的核(数据加强版)

【DFS好题】BZOJ1999- [Noip2007]Core树网的核(数据加强版)

NOIP的数据好水,一开始有好几个错结果NOIP数据就水过了??

【题目大意】

求无根树的直径上一段不超过S长的链,使得偏心距最小。具体概念见原题。

【思路】

首先明确几个性质:

(1)对于树中的任意一点,距离其最远的点一定是树的直径的某一端点。

(2)所有的直径是等价的,即任意一条所能求出的该最小偏心距相等。

于是我们可以用两次dfs求出直径。任取一个点找到离它最远的点r,再从r找到距离它最远的点l。l到r的路径就是直径。

显然在长度不超过S的情况下,链最长最好。在l到r上维护尽可能长的链,找到左右端点到直径做右端点的较大值的最小值。然后由链上各个点出发,找到不经过直径上的点抵达的其他点的最大深度。这个最大深度和之前的最小值中较大的就是答案。

为什么是整条直径上找最大深度,而不是在核上找呢?

显然,如果这个深度最深的点不是从核中的点,那么它到核的距离必定小于核的端点到直径端点的距离。所以如果有个节点到核的距离小于核的端点到直径端点的距离,那么它必定是从核上延伸出去的。

【错误点】

具体见程序。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int MAXN=500000+50;
 4 const int INF=0x7fffffff;
 5 struct edge
 6 {
 7     int to,len;
 8 };
 9 vector<edge> E[MAXN];
10 int n,s;
11 int l,r,dis[MAXN],f[MAXN],ban[MAXN];
12  
13 void addedge(int u,int v,int w)
14 {
15     E[u].push_back((edge){v,w});
16     E[v].push_back((edge){u,w});
17 }
18  
19 void init()
20 {
21     scanf("%d%d",&n,&s);
22     for (int i=1;i<n;i++)
23     {
24         int u,v,w;
25         scanf("%d%d%d",&u,&v,&w);
26         addedge(u,v,w);
27     }
28 }
29  
30 void dfs(int u,int fa)
31 {
32     f[u]=fa;
33     for (int i=0;i<E[u].size();i++)
34     {
35         int to=E[u][i].to;
36         if (ban[to] || to==f[u]) continue;
37         dis[to]=dis[u]+E[u][i].len;
38         dfs(to,u);
39     }
40 }
41  
42 void getd()
43 {
44     memset(ban,0,sizeof(ban));
45      
46     l=1,r=1;
47     dfs(l,0);
48     for (int i=1;i<=n;i++) if (dis[i]>dis[r]) r=i;
49      
50     l=r;
51     dis[r]=0;
52     dfs(r,0);
53     for (int i=1;i<=n;i++) if (dis[i]>dis[l]) l=i;
54 }
55  
56 void solve()
57 {
58     int i=l,j=l,ans=INF;
59     for (;i;i=f[i])
60     {
61         while (f[j] && dis[i]-dis[f[j]]<=s) j=f[j];
62         ans=min(ans,max(dis[j],dis[l]-dis[i]));
63         //每次找到以i为一个端点的最接近于S的链
64         //比较两端点和直径端点的长度 
65     }
66     for (i=l;i;i=f[i]) ban[i]=1;
67     //由于要找出不经过直径的最大深度,所以禁止访问直径上的点 
68     for (int i=l;i;i=f[i]) dis[i]=0,dfs(i,f[i]);
69     //★★★★★★★注意这里i的父亲必须传进去f[i],否则就修改了直径 
70     for (int i=1;i<=n;i++) ans=max(ans,dis[i]);
71     printf("%d",ans);
72 }
73  
74 int main()
75 {
76     init();
77     getd();
78     solve();
79     return 0;
80 } 

 

【DFS好题】BZOJ1999- [Noip2007]Core树网的核(数据加强版)