DFS
题目链接 Whistle's New Car
题目的意思是给定 n
个城市编号 1-n
,每个城市都有一个权值
,两个城市间的距离 这些城市的结构是一颗以 1
为根节点的树,输出每个城市i
的attractiveness
,每个节点 attractiveness
是满足以下条件的城市的个数
j
是i
的子树。- 在城市
j
加满 的油后不再加油能到达 i 城市。
主要思路:从根节点1
出发DFS整棵树,我们用s[i]
表示根节点到第i
个节点的路径长度之和,(第 i
个节点貌似不恰当,可以理解为第 i
层).num
数组保存了我们 dfs
过程中之前所经过的城市(后面有用)。对于当前城市i
它只能往上走,我们需要判断它能往上走到哪里,我们可以发现,对于在i
之前的城市j
如果满足 s[j]+x[i]>=s[i]
,那么我们就可以从城市i
走到城市j
,因此我们需要找到最小的j
,满足 s[j]>=s[i]-x[i]
,对于这个我们可以二分查找s
数组,找到满足条件的最小的j
,j
城市满足条件,j
到i
路上所有的城市节点都满足条件(不包括i),因此我们把j,j+1....i-1
这些城市答案都加1
,对于闭区间[j~i-1]
的值加上1
,可以类似于差分来处理,cnt[i-1]++
,cnt[j-1]--
.这里让cnt[j-1]--
,是为消除城市节点j
之前的城市的影响,因为我们每次统计答案是cnt[u] += cnt[v]
(v
是u
的孩子),对[j~i-1]
之间的城市我们这样操作后它们的答案都会加1
,但j
之前的城市我们不能让它答案加1
,因此我们cnt[j-1]--
来消除这个影响。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<vector>
using namespace std;
#define lowbit(x) (x&(-x))
typedef long long ll;
const int N = 5e5 + 10;
int n;
ll a[N],s[N];
typedef pair<int, ll> P;
vector<P> p[N];
int cnt[N],num[N];
int ret;
void dfs(int u, int pre)
{
for (auto E : p[u])
{
int v = E.first;
ll w = E.second;
if (v == pre) continue;
s[ret] = s[ret - 1] + w;
num[ret] = v; //路径上第ret城市为v
int pos = lower_bound(s, s + ret + 1, s[ret] - a[v])-s;
if (pos < ret)//如果能到达父结点城市
{
cnt[u]++;
if (pos) cnt[num[pos - 1]]--;//这个一定要减,不然后面节点的数加到父节点的时候,父节点就多加了
}
ret++;
dfs(v, u);
ret--;
cnt[u] += cnt[v];
}
}
int main()
{
freopen("car.in", "r", stdin);
int T;
scanf("%d", &T);
while (T--)
{
ret = 1;
memset(cnt, 0, sizeof(cnt));
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%lld", &a[i]);
p[i].clear();
}
for (int i = 1; i < n; i++)
{
int u,v;
ll w;
scanf("%d%d%lld", &u, &v, &w);
p[u].push_back(P(v, w));
p[v].push_back(P(u, w));
}
ret = 1;
num[0] = 1;
s[0] = 0;
dfs(1, -1);
for (int i = 1; i <= n; i++)
printf("%d%c", cnt[i], i == n ? '\n' : ' ');
}
fclose(stdin);
return 0;
}
评论