Long chain subdivision
On the whole, it is very similar to heavy chain dissection
First, define the heavy son as: the son with the deepest subtree depth
Then the rest is the light son
The heavy side is connected to the heavy son, and the heavy side is connected into a heavy chain
Each point is in a unique long chain, and the long chain must not intersect
The realization is similar to heavy chain subdivision
len records the deepest depth that can be reached
void dfs(int u,int _f){ fe(i,u) { int v = e[i].to; if (v != _f){ dfs(v,u); if(len[v] > len[son[u]]) son[u] = v; } } len[u] = len[son[u]] + 1; }
Optimize DP
When maintaining depth related information, we should quickly inherit the information from the son, and then count the information from the son with violence, which is very similar to dsu on tree
This is not particularly clear about depth. In fact, it is the feeling that subscript is related to depth, not anything else
Because each point belongs to only one long chain and all the light sons are the top of the long chain, the time complexity is linear
CF161D Distance in Tree
Find the number of paths with exactly \ (k \) length in a rootless tree
Dot divide and conquer is \ (\ mathrm{O}(n\log n) \), can it be better?
Consider the long chain subdivision to optimize the simple tree DP
Let \ (f_{i,j} \) represent the number of nodes whose distance from \ (I \) to \ (I \) is \ (j \)
Because \ (k \le 500 \), the principle of violent transfer and then multiplication can pass this problem
Complexity \ (\ mathrm{O} (nk) \)
The optimized complexity is \ (\ mathrm{O} (n) \)
For the convenience of inheriting information, use the pointer ptr[i] to point to the information inherited by the point \ (I \)
This also makes the statistical information only use an array, and the pointer only represents the first position of a node information
Then this thing is as fast and outrageous as my tree array + divide and conquer because of violence
The optimal solution of this problem is dsu on tree. Do you do whatever you want when the constant is small
Code :
int head[N],ecnt = -1; struct Edge { int nxt,to; }e[N << 1]; inline void AddEdge(int st,int ed) { e[++ecnt] = (Edge) {head[st],ed},head[st] = ecnt; e[++ecnt] = (Edge) {head[ed],st},head[ed] = ecnt; } int k; int ans; int fa[N]; int len[N],hson[N]; int *ptr[N],buc[N]; int cnt; void dfs1(int u,int _f) { fa[u] = _f; fe(i,u) { int v = e[i].to; if(v == _f) continue; dfs1(v,u); if(len[v] > len[hson[u]]) hson[u] = v; } len[u] = len[hson[u]] + 1; } inline void Add(int u,int v) { ff(i,std::max(0,k - len[u]),std::min(len[v],k)) ans += ptr[v][i] * ptr[u][k - i - 1]; ff(i,0,len[v]) ptr[u][i + 1] += ptr[v][i]; } void dfs2(int u,int top) { if(u == top) { ptr[u] = buc + cnt; cnt += len[u]; } else ptr[u] = ptr[fa[u]] + 1; fe(i,u) { int v = e[i].to; if(v == fa[u] || v == hson[u]) continue; dfs2(v,v); } if(hson[u]) dfs2(hson[u],top); if(k < len[u]) ans += ptr[u][k]; ++ptr[u][0]; fe(i,u) { int v = e[i].to; if(v == fa[u] || v == hson[u]) continue; Add(u,v); } }
CF1009F Dominant Indices
It seems that this question is more convenient than dsu on tree
[POI2014] HOT-Hotels
This question is different from the above two questions and cannot be combined with tree heuristic
For those not strengthened, violence \ (\ mathrm{O} (n^2)\) DP is enough
But the enhanced version won't work
Then design the status:
\(f_{i, j} \): the number of points within the \ (I \) subtree whose distance to \ (I \) is \ (j \)
\(g_{i, j} \): the logarithm of points within the \ (I \) subtree whose distance to \ (I \) is \ (j \)
Then the principle of multiplication
It seems to be too hasty
Look at the code
Did I get wind_ Whosper syndrome
Code :
int head[N],ecnt = -1; struct Edge { int nxt,to; }e[N << 1]; inline void AddEdge(int st,int ed) { e[++ecnt] = (Edge) {head[st],ed},head[st] = ecnt; e[++ecnt] = (Edge) {head[ed],st},head[ed] = ecnt; } int n; int len[N],son[N]; ll tmp[N << 2]; ll *f[N],*g[N],*id = tmp; ll ans; void dfs(int u,int _f){ fe(i,u) { int v = e[i].to; if (v != _f){ dfs(v,u); if(len[v] > len[son[u]]) son[u] = v; } } len[u] = len[son[u]] + 1; } void dp(int u,int _f){ if(son[u]) { f[son[u]] = f[u] + 1; g[son[u]] = g[u] - 1; dp(son[u],u); } f[u][0] = 1; ans += g[u][0]; fe(i,u) { int v = e[i].to; if(v == _f || v == son[u]) continue; f[v] = id; id += len[v] << 1; g[v] = id; id += len[v] << 1; dp(v,u); ff(j,0,len[v] - 1){ if(j) ans += f[u][j - 1] * g[v][j]; ans += g[u][j + 1] * f[v][j]; } ff(j,0,len[v] - 1){ g[u][j + 1] += f[u][j + 1] * f[v][j]; if(j) g[u][j - 1] += g[v][j]; f[u][j + 1] += f[v][j]; } } } int mian() { init_IO(); mems(head,-1); n = read(); ff(i,1,n - 1) AddEdge(read(),read()); dfs(1,1); f[1] = id; id += len[1] << 1; g[1] = id; id += len[1] << 1; dp(1,0); write(ans); end_IO(); return 0; }
Optimization greed
BZOJ 3252 introduction
Summary of topic meaning:
A tree with \ (n \) points has roots and points have point weights. Select \ (k \) simple paths from the root node to the leaf node to maximize the sum of point weights of the coalescence of these paths
Find the weight sum of this point
Then you can find that this structure is very similar to the structure of the long chain. The long chain also does not intersect, so the contribution will not be counted many times, and the long chain also reaches the leaf node
So take the one with large weight, find the first \ (k \) and open a heap
Find k-level ancestors
Given a rooted tree, multiple queries are given. The form \ (u,k \) represents the ancestor of jumping up \ (k \) nodes from the seeking point \ (u \)
First of all, the direct tree multiplication is \ (< \ mathrm {o} (n \ log n), \ mathrm {o} (\ log n) > \)
Try to optimize it to \ (< \ mathrm {o} (n \ log n), \ mathrm {o} (1) > \)
First, the tree is doubled, but only the highest binary bit of \ (k \) is skipped when asking, that is to skip \ (2^{\lfloor \log k \rfloor} \) steps
Chain head preprocessing violence jumps up and down the chain to reach the point
Then jump to the head of the long chain and solve it by preprocessing information
Looking for \ (k \) ancestors can't find any practical problems A search is full of questions that can be done by dsu on tree and tree DP
I hope I can use it in the future
Code :
The template question directly gives the father of each point, so there is no need to add inverse edges, and no value is passed into the father node during dfs
Don't forget to add the opposite side when writing normally
And my template constant is so big
int head[N],ecnt = -1; struct Edge { int nxt,to,w; }e[N]; inline void AddEdge(int st,int ed) { e[++ecnt] = (Edge) {head[st],ed}; head[st] = ecnt; } int dep[N]; int anc[N][20]; int hson[N],len[N],top[N]; std::vector<int> upp[N],low[N]; void dfs1(int u) { len[u] = dep[u] = dep[anc[u][0]] + 1; fe(i,u) { int v = e[i].to; anc[v][0] = u; for(int j = 0;anc[v][j];++j) anc[v][j + 1] = anc[anc[v][j]][j]; dfs1(v); if(len[v] > len[u]) len[u] = len[v],hson[u] = v; } } void dfs2(int u,int t) { top[u] = t; if(u == t) { int p = u; ff(i,0,len[u] - dep[u]) upp[u].push_back(p),p = anc[p][0]; p = u; ff(i,0,len[u] - dep[u]) low[u].push_back(p),p = hson[p]; } if(hson[u]) dfs2(hson[u],t); fe(i,u) { int v = e[i].to; if(v == hson[u]) continue; dfs2(v,v); } } int l2[N]; inline int query(int u,int k) { if(!k) return u; u = anc[u][l2[k]]; k -= (1 << l2[k]),k -= dep[u] - dep[top[u]]; u = top[u]; return k >= 0 ? upp[u][k] : low[u][-k]; }