CodeForces 682C Alyona and the Tree (树上DFS)

题意:给定一棵树,每个叶子有一个权值,每条边也有一个权值,现在让你删最少的结点,使得从任何结点出发到另一个结点的边上权值和都小于两个结点的权值。

析:很明显是DFS,不过要想找出最少的结点可能不太容易,所以我们可以先找出剩下结点最多,那么用总数减去这个就好,那么怎么找哪些结点是剩下的呢?首先要知道,如果一个结点要被删掉,

那么它的子树肯定也要被删掉,并且,要满足dist(v, u) <= a[u]才是可能留下的,那么只要dist(v, u) >a[u],就不要,所以一定要注意的是,结点的距离可能为负,所以要在DFS时判断一下,肯定是取非负的,

想一想为什么。

代码如下:

#include <bits/stdc++.h>

using namespace std;
const int maxn = 1e5 + 5;
typedef long long LL;
typedef pair<int, int> P;
int a[maxn];
vector<P> G[maxn];
int ans;

void dfs(int u, LL d, int fa){
    if(a[u] < d)  return ;//不满足要删掉
    ++ans;//计算最多有多少结点
    for(int i = 0; i < G[u].size(); ++i)
        if(G[u][i].first == fa)  continue;//不能返回父结点
        else   dfs(G[u][i].first, max(0LL, d+G[u][i].second), u);//判断是不是大于等于0
}

int main(){
    int n, p, c;
    cin >> n;
    for(int i = 1; i <= n; ++i) cin >> a[i];
    for(int i = 1; i < n; ++i){
        cin >> p >> c;
        G[i+1].push_back(P(p, c));
        G[p].push_back(P(i+1, c));
    }
    ans = 0;
    dfs(1, 0, -1);
    printf("%d\n", n - ans);
    return 0;
}
时间: 2024-10-29 10:46:09

CodeForces 682C Alyona and the Tree (树上DFS)的相关文章

Codeforces 739B Alyona and a tree (树上路径倍增及差分)

题目链接 Alyona and a tree 弄了好几个小时终于Accepted了,之后发现这个题是Div1的. 比较考验我思维的一道好题. 首先,做一遍DFS预处理出t[i][j]和d[i][j].t[i][j]表示从第i个节点到离他第2^j近的祖先,d[i][j]表示从i开始到t[i][j]的路径上的路径权值总和. 在第一次DFS的同时,对节点x进行定位(结果为dist(x, y)<=a(y))的离x最远的x的某个祖先,然后进行O(1)的差分. 第一次DFS完成后,做第二次DFS统计答案(统

codeforces 682C Alyona and the Tree DFS

这个题就是在dfs的过程中记录到根的前缀和,以及前缀和的最小值 #include <cstdio> #include <iostream> #include <ctime> #include <vector> #include <cmath> #include <map> #include <stack> #include <queue> #include <algorithm> #include

Codeforces 682C Alyona and the Tree(树形DP)

题目大概说给一棵点有权.边也有权的树.一个结点v不高兴当且仅当存在一个其子树上的结点u,使得v到u路径上的边权和大于u的权值.现在要不断地删除叶子结点使得所有结点都高兴,问最少删几个叶子结点. 一开始题目看错了,以为说的是v到u路径上的边权和小于v的权值,然后想出了个解法:从根开始DFS,找高兴的结点,递归过程中在set插入各个祖先结权值,递归返回时从set中删除,而如果set里面最小的元素小于当前结点的路径和那么这个结点就不能要直接return,另外还用到一个简单的数学原理——两个数同时加上相

CodeForces 682C Alyona and the Tree(广搜 + 技巧)

方法:从根节点开始广搜,如果遇到了应该删除的点,就再广搜删掉它的子树并标记,然后统计一下被标记的个数就是答案,所谓技巧就是从根节点开始搜索的时候,如果遇到了某个节点的距离<0,就让它是0,0可以消除负数效应,让后面的点重新参与正常删除操作,这个方法的正确性不难证明,可以自己画一下图.而且还有比较方便的方法就是,记录不被删除的点,然后n-他们就可以了. 代码如下: #include<iostream> #include<cstdio> #include<cstring&g

CF739B Alyona and a tree 树上差分

题目描述 \(Alyona有一棵有 n个节点的树.这棵树的根节点是 1.在每个节点里,Alyona写了一个正整数,在节点 i 她写了正整数a_i .另外,她在这棵树上的每条边上写了一个正整数(不同边上可能有不同的数). 让我们定义 dist(v,u) 作为从 v 到 u 的简单路径上的边权和. 当且仅当 u 在 v 的子树中并且 dist(v,u)<=a_u,顶点 v 控制顶点 u(v!=u) .Alyona想在某些顶点定居.为了做到这件事,她想知道在每个节点 v 能控制几个节点.\) 对于\(

CodeForces - 963B Destruction of a Tree (dfs+思维题)

B. Destruction of a Tree time limit per test 1 second memory limit per test 256 megabytes input standard input output standard output You are given a tree (a graph with n vertices and n?-?1 edges in which it's possible to reach any vertex from any ot

CodeForces 681D Gifts by the List (树上DFS)

题意:一个家庭聚会,每个人都想送出礼物,送礼规则是, 一个人,先看名单列表,发现第一个祖先 就会送给他礼物,然后就不送了,如果他没找到礼物 他会伤心的离开聚会!告诉你m个祖先关系, 和每个人想给谁送!让你求出名单列表! 析:这个题,真是没想到啊,还是看的题解,首先要知道的是,如果自己和父结点送的人不一样,并且自己不是给自己的,那么就是无解,为什么呢?是这样的,假设自己和父结点送的人不一样,并且也不是给自己的, 那么一定是给自己的某个祖先,而父结点也是自己的某个祖先,那么不是同一个人,必定一个是另

CodeForces - 383C Propagating tree(dfs + 线段树)

题目大意: 给出一棵树,树上每个节点都有权值,然后有两个操作. 1 x val 在结点x上加上一个值val,x的儿子加上 -val,x的儿子的儿子加上 - (-val),以此类推. 2 x 问x节点的值. 思路分析: 每个节点上加值都是给自己的儿子节点加,而且这个是颗树. 比如样例上的,如果你给node 1加一个值,那么五个节点都加. 再给node 2加个值,2的儿子节点也加了,之前给1加的值也要加到2号节点的儿子. 所以你会发现节点的儿子会存在一个从属的关系. 这样的话,我们可以把所有节点从新

Codeforces 29D Ant on the Tree 树的遍历 dfs序

题目链接:点击打开链接 题意: 给定n个节点的树 1为根 则此时叶子节点已经确定 最后一行给出叶子节点的顺序 目标: 遍历树并输出路径,要求遍历叶子节点时按照给定叶子节点的先后顺序访问. 思路: 给每个节点加一个优先级. 把最后一个叶子节点到父节点的路径上的点优先级改为1 把倒数第二个叶子节点到父节点的路径上的点优先级改为2 如此每个点就有一个优先级,每个访问儿子节点时先访问优先级大的即可 对于无解的判断:得到的欧拉序列不满足输入的叶子节点顺序即是无解. #include <cstdio> #