codeforces 337D Book of Evil

对每个节点来说距离标记点的最大值  有俩个来源,一个是子树,一个来自父节点。   对于子树,一次dfs可以即可,那么通过父节点的 也有俩个可能   一个是兄弟节点,还有一个的父节点的父。

以下

官方题解

337D - Book of Evil

Obviously, in graph theory language our problem is: given a tree with n vertices, m of which are marked, find the number of vertices which are at most distance d apart from each of the marked vertices.

Let us hang the tree by some vertex r, that is, assume that it is a rooted tree with root in vertex r. Let us also rephrase the condition imposed on sought vertices: we need to count such vertices v that the maximum distance from v to a marked vertex is at most d.

For any inner vertex v, the marked vertex which is the farthest from it is either in the subtree of v or outside it — in the latter case the path from v to the farthest marked vertex traverses the parent of v. Using this observation, we can recompute the distances to the farthest marked vertices when transiting from a vertex to its child.

First, we will compute the distance from every vertex v to the farthest marked vertex within the subtree of v. Let us call this distancedistDown[v]. The values of distDown[] can be computed in a single depth-first search: for each leaf of the tree this distance is either 0 (when the leaf is marked) or nominal negative infinity (when the leaf is not marked), and for each inner vertex vdistDown[v]=max(distDown[child1], distDown[child2], ..., distDown[childK])+1, where childi are the children ofv.

Now we will compute the distances from each vertex to the farthest marked vertex outside its subtree. Let's denote this distance withdistUp[v]. We will use DFS again to compute values of distUp[]. For the root, distUp[r] is equal to nominal negative infinity, and for any other vertex v there are two cases: either the farthest marked vertex is located in the subtree of v-s parent p, or it is even "farther", i.e., the path to it traverses vertex p-s parent. In the first case, the distance from v to such vertex is equal tomax(distDown[sibling1], ..., distDown[siblingK])+2, where siblingi are the brothers (other children of the parent) of vertex v. In the second case, it is equal to distUp[p]+1. Thus, distUp[v] is equal to the maximum of these two values. Note that you need to be clever to perform the computations in the first case in overall linear time. For this, you can find the maximum max1and second maximum max2 of values distDown[sibling1], ..., distDown[siblingK]. After that, whendistDown[v] < max1, we have max(distDown[sibling1], ..., distDown[siblingK])=max1, otherwise we havedistDown[v] = max1 and max(distDown[sibling1], ..., distDown[siblingK])=max2.

After computing distDown[] и distUp[], it is easy to derive the answers: it is the count of such vertices v thatdistDown[v] <= d && distUp[v] <= d.

You can check 4302127 for an implementation of the described approach.


#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <vector>

using namespace std;

const int maxn = 100010;
const int inf = 0x7f7f7f7f;
int up[maxn],down[maxn][2];
bool vis[maxn];
vector<int>g[maxn];
void dfs1(int u,int p)
{
    down[u][0]=down[u][1]=up[u]=-inf;
    if(vis[u]) down[u][0]=down[u][1]=up[u]=0;
    int sz=g[u].size();
    for(int i=0;i<sz;i++)
    {
        int v=g[u][i];
        if(v==p) continue;
        dfs1(v,u);
        if(down[u][0]<down[v][0]+1)
        {
            down[u][1]=down[u][0];
            down[u][0]=down[v][0]+1;
        }
        else if(down[u][1]<down[v][0]+1) down[u][1]=down[v][0]+1;
    }
}
void dfs2(int u,int p)
{
    int sz=g[u].size();
    for(int i=0;i<sz;i++)
    {
        int v=g[u][i];
        if(v==p) continue;
        if(down[v][0]+1!=down[u][0]) up[v]=down[u][0]+1;
        else up[v]=down[u][1]+1;
        up[v]=max(up[v],up[u]+1);
        dfs2(v,u);
    }
}

int main()
{
    int n,m,d;
    while(~scanf("%d%d%d",&n,&m,&d))
    {
        memset(vis,0,sizeof(vis));
        for(int i=0;i<=n;i++) g[i].clear();
        for(int i=0;i<m;i++)
        {
            int x;
            scanf("%d",&x);
            vis[x]=1;
        }
        for(int i=1;i<n;i++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            g[u].push_back(v);
            g[v].push_back(u);
        }
        dfs1(1,0);
        dfs2(1,0);
        int ans=0;
        for(int i=1;i<=n;i++) if(down[i][0]<=d&&up[i]<=d) ans++;
        printf("%d\n",ans);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值