#DFS序+二分+字首和# Codeforces Round #381 (Div. 1) B. Alyona and a tree
題目連結
B. Alyona and a tree
time limit per test 2 seconds
memory limit per test 256 megabytes
input standard input
output standard output
Alyona has a tree with n vertices. The root of the tree is the vertex 1. In each vertex Alyona wrote an positive integer, in the vertex i
Let's define dist(v, u) as the sum of the integers written on the edges of the simple path from v to u.
The vertex v controls the vertex u
Alyona wants to settle in some vertex. In order to do this, she wants to know for each vertex v what is the number of vertices u such that vcontrols u.
Input
The first line contains single integer n
The second line contains n integers a1, a2, ..., an (1 ≤ ai ≤ 109) — the integers written in the vertices.
The next (n - 1) lines contain two integers each. The i-th of these lines contains integers pi and wi (1 ≤ pi ≤ n, 1 ≤ wi ≤ 109) — the parent of the (i + 1)-th vertex in the tree and the number written on the edge between pi and (i + 1).
It is guaranteed that the given graph is a tree.
Output
Print n integers — the i-th of these numbers should be equal to the number of vertices that the i-th vertex controls.
Description:
給出一顆由n個節點構成的帶權樹,求每個節點可以控制的節點數,v節點控制 u 節點的條件:dist(v, u) <= a[u]。
Solution:
假設 dis[x] 表示根節點到 x 節點上的邊權和,則 x 這個節點被 u 節點控制的條件為:dis[x] - dis[u] <= a[x], 即 dis[x] - a[x] <= dis[u]。 由於 dis陣列是通過DFS序來維護的,所以一定是單調遞增的,那麼就可以利用二分找出dis陣列中第一個滿足 dis[x] - a[x] <= dis[u] 的u節點,那麼 u → x 的路徑上的所有節點都能夠控制 x 節點,而 u 節點以上的節點都不能控制x節點,所以在這裡可以引入一個利用字首和記錄答案的思想:假設在當前已找到一條路徑為 u → x ,其中 u 節點的父節點為 fa[u],x節點的父節點為 fa[x],那麼令 ans[fa[x]]++,ans[fa[u]]--(ans[fa[x]]++這一語句可以使用另一種記錄方法:將 ans 陣列在一開始時就賦值為1,然後再最後答案處都減一也可),然後在DFS序的時候累加答案記錄字首和即可。
Implementation:
DFS序維護 dis陣列和記錄 ans陣列的字首和
二分查詢每個節點的合法路徑
字首和(或者是樹上差分思想)維護答案
Code:
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
#define Fio ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
#define fopen freopen("in.txt", "r", stdin); freopen("out.txt", "w", stdout);
#define mst(a, b) memset(a, b, sizeof(a))
#define _rush() int T; cin >> T; while(T--)
#define rush() int T; scanf("%d", &T); while(T--)
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
typedef pair<int, LL> PIL;
typedef pair<LL, int> PLI;
const int INF = 0x3f3f3f3f;
const double eps = 1e-9;
const int Mod = 1e9 + 7;
const int MaxN = 2e5 + 5;
vector <PIL> G[MaxN];
vector <PLI> path;
LL a[MaxN], dis[MaxN];
int vis[MaxN], ans[MaxN];
void DFS(int x, int fa) {
vis[x] = 1;
//ans[x]++; //選擇在一開始時將ans陣列賦值1
int pos = lower_bound(path.begin(), path.end(), PLI(dis[x] - a[x], 0)) - path.begin(); //二分查詢第一個合法u節點
if(--pos >= 0) ans[path[pos].second]--; //u節點的父節點(第一個不合法的節點)減一
ans[fa]++; //(如果使用一開始將ans賦值為1,不需要這句)當前查詢節點的父節點加一,u —> fa這些節點都可控制當前節點,所以答案加一
path.push_back(PLI(dis[x], x)); //當前路徑壓入查詢路徑中
for(int i = 0; i < G[x].size(); i++) {
int u = G[x][i].first; LL w = G[x][i].second;
if(vis[u]) continue;
dis[u] = dis[x] + w;
DFS(u, x);
ans[x] += ans[u]; //字首和維護答案
}
path.pop_back(); //維護查詢路徑的根節點
}
int main()
{
Fio;
int n;
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 2; i <= n; i++) {
int u; LL d;
cin >> u >> d;
G[i].push_back(PIL(u, d));
G[u].push_back(PIL(i, d));
}
DFS(1, 0);
//for(int i = 1; i <= n; i++) cout << ans[i] - 1 << " ";
for(int i = 1; i <= n; i++) cout << ans[i] << " ";
cout << endl;
return 0;
}