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;
}



CodeForces - 616D是一个关于找到一个序列中最长的第k好子段的起始位置和结束位置的问题。给定一个长度为n的序列和一个整数k,需要找到一个子段,该子段中不超过k个不同的数字。题目要求输出这个序列最长的第k好子段的起始位置和终止位置。 解决这个问题的方法有两种。第一种方法是使用尺取算法,通过维护一个滑动窗口来记录\[l,r\]中不同数的个数。每次如果这个数小于k,就将r向右移动一位;如果已经大于k,则将l向右移动一位,直到个数不大于k。每次更新完r之后,判断r-l+1是否比已有答案更优来更新答案。这种方法的时间复杂度为O(n)。 第二种方法是使用枚举r和双指针的方法。通过维护一个最小的l,满足\[l,r\]最多只有k种数。使用一个map来判断数的种类。遍历序列,如果当前数字在map中不存在,则将种类数sum加一;如果sum大于k,则将l向右移动一位,直到sum不大于k。每次更新完r之后,判断i-l+1是否大于等于y-x+1来更新答案。这种方法的时间复杂度为O(n)。 以上是两种解决CodeForces - 616D问题的方法。具体的代码实现可以参考引用\[1\]和引用\[2\]中的代码。 #### 引用[.reference_title] - *1* [CodeForces 616 D. Longest k-Good Segment(尺取)](https://blog.csdn.net/V5ZSQ/article/details/50750827)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Codeforces616 D. Longest k-Good Segment(双指针+map)](https://blog.csdn.net/weixin_44178736/article/details/114328999)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值