[CodeForces - 739B] Alyona and a tree 【树 + 差分 + 二分】
作者:互联网
题意:
- 给定一颗树,每个结点有对应点权,每条边也有对应边权。如果dis<v, u> <= val[u],则说明结点u被结点v所管辖。对于每个结点,输出它管辖的结点数目。
思路:
我们定义dis[ u ]为结点u到根结点的边权和。显而易见dis<v, u> = dis[u] - dis[v],并且dis是递增的
无根树转有根树,那么对于任何一条树链,树链上结点u,我们要找到dis[u] - dis[v] <= val[u],转化一下,也就是我们需要找到结点v,并且有dis[v] >= dis[u] - val[u]。由于dis[ ]的单调递增性,我们可以二分找到第一个大于等于dis[u] - val[u]的结点v,那么<v, fa(u)>中每个结点都管辖着结点u. 也就是树链<v, fa(u)>上每个结点的答案都应该加1.
显然我们不能这么做,复杂度爆炸啊。于是想到差分,对于每一条从根结点到叶子节点的树链,我们都看成一个线性的数组。那么上述v就是右端点,fa(u)就是左端点。于是我们只需要O(1)将 -- sum[v + 1], ++ sum[fa(v)]即可,最后回溯的时候sum[u] += sum[son(u)]得到答案啦~~~~
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <cstring>
#define INF 0x3f3f3f3f3f
using namespace std;
typedef long long ll;
inline int read()
{
int x = 0, f = 1; char c = getchar();
while(c < '0' || c > '9') { if(c == '-') f = -f; c = getchar(); }
while(c >= '0' && c <= '9') { x = x * 10 + c - '0'; c = getchar(); }
return x * f;
}
const int maxN = 200005;
const int maxM = 1000006;
int n, chain[maxN];
ll val[maxN], dis[maxN];
ll que[maxN], cnt; //模拟队列,存储当前树链的结点的dis
int sum[maxN];
vector<pair<int, ll> >edge[maxN];
void dfs(int u, int fa)
{
que[++cnt] = dis[u];
chain[cnt] = u;
int id = lower_bound(que + 1, que + cnt, dis[u] - val[u]) - que;
//因为需要dis[u] - dis[v] <= val[], 也就是找到dis[v] >= dis[u] - val[u],那么路径<v, u>上u的每个祖先都要增加1
-- sum[chain[id - 1]]; //v相当于右端点,右端点+1执行-1操作
++ sum[fa]; //左端点执行+1操作
for(auto e : edge[u] )
{
int v = e.first;
dis[v] = dis[u] + e.second;
dfs(v, u);
sum[u] += sum[v];
}
-- cnt;
}
int main()
{
n = read();
for(int i = 1; i <= n; ++ i ) val[i] = read();
for(int v = 2; v <= n; ++ v)
{
int u, w;
u = read(); w = read();
edge[u].push_back(make_pair(v, w));
}
dfs(1, 0);
for(int i = 1; i <= n; ++ i )
printf("%d%c", sum[i], " \n"[i == n]);
return 0;
}
Eve_Miracle* 发布了242 篇原创文章 · 获赞 68 · 访问量 2万+ 私信 关注
标签:结点,int,sum,tree,Alyona,CodeForces,端点,include,dis 来源: https://blog.csdn.net/weixin_44049850/article/details/104595723