@
subject
For a tree, there is an obstacle at each point. The obstacles will move periodically in the order of parent \ (\ to \) subtree 1\(\to \) parent \ (\ to \) subtree 2\(\to \) parent \ (\ cdots \ to \) parent. A person will move to the parent node every second starting from the \ (x \) node. Ask how many obstacles will be encountered when moving to the root node
[the external chain image transfer fails. The source station may have an anti-theft chain mechanism. It is recommended to save the image and upload it directly (img-7gctsbl6-1636341914385) (C: \ users \ administrator \ appdata \ roaming \ typora \ typera user images \ image-20211108104439427. PNG)]
thinking
Nature: if an obstacle is encountered, it will only meet in the first cycle of the obstacle's movement
In addition, if you have written the LCA of RMQ version, you will know that the length of a cycle for the obstacles initially at \ (i \) is: the size of the subtree with \ (i \) as the root multiplied by 2 minus 1 (except the root, each point produces two contributions from itself and its father)
As shown in the figure, the obstacles at point 3 are handed down from No. 1 and self-contained on No. 3. The first time the two obstacles appear on No. 3 is 0,1, and then the two obstacles traverse No. 4 and return to No. 3. Therefore, the second time they appear is 2,3. Similarly, the third time they appear is 4,5. If No. 5 is the starting point, let's check whether there are obstacles at No. 3 at time 1
Note that the movement of these obstacles is the same, so for the same \ (i \), the time difference between the \ (i\),\(i+1 \) times of different obstacles traversing the same point in a cycle is the same
We traverse each point on the path from the root to the starting point in turn, and a set \ (T \) represents the set of times when all obstacles on the current path appear at the current point for the first time. Each step down is equivalent to adding a number to all the numbers in \ (T \), and then putting 0 into the set
Since the same number is added at the same time, we can maintain it with a lazy object
The time complexity can be \ (O(n) \)
code
#include <iostream> #include <cstdio> #include <vector> #include <unordered_map> #include <cstring> using namespace std; int read() { int re = 0; char c = getchar(); bool negt = false; while(c < '0' || c > '9')negt |= (c == '-') , c = getchar(); while(c >= '0' && c <= '9')re = (re << 1) + (re << 3) + c - '0' , c = getchar(); return negt ? -re : re; } const int N = 5e5 + 10; int n; int siz[N] , fa[N]; int start; bool flag[N]; int dis[N]; vector<int> son[N]; vector<int> vis;//vis: the point through which the path from the root to the starting point passes void dfs(int x) { for(auto to : son[x]) dfs(to) , siz[x] += siz[to]; ++siz[x]; } unordered_map <int , int> mp; inline int mp_find(int val) { auto p = mp.find(val); return p == mp.end() ? 0 : p->second; } void solve() { memset(siz , 0 , sizeof(siz)) , memset(flag , 0 , sizeof(flag)) , memset(dis , 0 , sizeof(dis)) , memset(fa , 0 , sizeof(fa)) , vis.clear(); mp.clear(); n = read(); for(int i = 1 ; i <= n ; i++)son[i].clear(); for(int i = 1 ; i <= n ; i++) for(int j = read() , to ; j > 0 ; j--) son[i].push_back(to = read()) , fa[to] = i; dis[start = read()] = 0; while(start != 0) { flag[start] = true; dis[fa[start]] = dis[start] + 1; vis.push_back(start); start = fa[start]; } for(int i = 0 , j = vis.size() - 1 ; i < j ; i++ , j--)swap(vis[i] , vis[j]); dfs(1); int lzyTag = -1; int ans = 0; for(int u : vis) { int sum = 0; ++lzyTag; mp[-lzyTag]++; for(int v : son[u]) {//Traversing a subtree from u will return to the U node again if(flag[v])break; sum += siz[v] * 2; ans += mp_find(dis[u] - sum - lzyTag); } ans += mp_find(dis[u] - lzyTag); lzyTag += sum; } cout << ans << endl; } int main() { freopen("run.in" , "r" , stdin); freopen("run.out" , "w" , stdout); int T = read(); while(T--)solve(); return 0; }