点分治
解决问题
对于某些限定条件的路径静态地进行统计的算法.
基本思路
分治思想
对于某一点P,树上路径可以分为两类,经过点P,包含与点P的某一棵子树上
第二种可以递归处理
第一种通过求P的所有子节点的深度就可以处理
树的重心
如果是一条链的话,那复杂度很大,需要递归N层,每个节点访问一次.
用重心求只需要\(log\)层
模板题① Tree
本题是求两点路径小于等于k
code
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; const int maxn = 1e4 + 7; const int inf = 0x3f3f3f3f; int k, tot, head[maxn]; bool vis[maxn]; int nowN, rt, maxP; int fin; int Size[maxn], dep[maxn]; struct Edge { int to, next, w; } e[maxn << 1]; vector<int> D; void init(int n) { tot = 0; for (int i = 0; i <= n; ++i) { head[i] = 0; vis[i] = 0; } fin = 0; rt = 0; } void get_rt(int u, int fa) { int max_part = 0; Size[u] = 1; for (int i = head[u], v; i; i = e[i].next) { v = e[i].to; if (vis[v] || v == fa) continue; get_rt(v, u); Size[u] += Size[v]; max_part = max(max_part, Size[v]); } max_part = max(nowN - Size[u], max_part); if (max_part < maxP) { maxP = max_part; rt = u; } } void get_dep(int u, int fa) { D.push_back(dep[u]); for (int i = head[u], v; i; i = e[i].next) { v = e[i].to; if (vis[v] || v == fa) continue; dep[v] = dep[u] + e[i].w; get_dep(v, u); } } int get_ans(int x) { D.clear(); get_dep(x, 0); sort(D.begin(), D.end()); int j = D.size() - 1, i = 0; int ans = 0; while (i < j) {//利用单调性 // cout<<D[i]<<" "<<D[j]<<endl; if (D[i] + D[j] <= k) { ans += j - i; i++; } else { j--; } } return ans; } void calc() { dep[rt] = 0; fin += get_ans(rt); vis[rt] = 1; for (int i = head[rt], v; i; i = e[i].next) { v = e[i].to; if (vis[v])continue; fin -= get_ans(v); nowN = Size[v]; maxP = inf; rt = 0; get_rt(v, 0); calc(); } } inline void add(int u, int v, int w) { tot++; e[tot].next = head[u], e[tot].to = v, e[tot].w = w; head[u] = tot; } int main() { int n; while (scanf("%d%d", &n, &k) && n && k) { init(n); for (int i = 1, u, v, w; i < n; ++i) { scanf("%d%d%d", &u, &v, &w); add(u, v, w), add(v, u, w); } maxP = inf; nowN = n; get_rt(1, 0); calc(); printf("%d\n", fin); } return 0; }
模板题②:点对游戏
- 本题是求两点路径等于k,再加上一下概率的操作.
- 因为取点是独立时间,看做超几何分布
- 期望等于\(p*sum\)
- \(\frac{C_{k}^2}{C_{n}^2}*sum\)
用\(f\)数组记录距离出现的次数
code
#include <bits/stdc++.h> using namespace std; template<class T> inline void read(T &res) { res = 0; T f = 1; char c = getchar(); while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); } while (c >= '0' && c <= '9') { res = res * 10 + c - '0'; c = getchar(); } res *= f; } const int maxn = 5e4 + 5; const int inf = 0x3f3f3f3f; struct Edge { int to, next; } e[maxn << 1]; int tot, head[maxn]; int n, m, Luck[15], nowN; int Size[maxn], ans, rt; int md[maxn]; int f[maxn], g[maxn]; int sum; bool vis[maxn]; void get_rt(int x, int fa) {//求重心 Size[x] = 1; int max_part = 0; for (int i = head[x], v; i; i = e[i].next) { v = e[i].to; if (v == fa || vis[v]) continue; get_rt(v, x); Size[x] += Size[v]; max_part = max(max_part, Size[v]); } max_part = max(max_part, nowN - Size[x]); if (max_part < ans) { ans = max_part; rt = x; } } void get_md(int x, int fa) {//求子树最大深度 md[x] = 0; for (int i = head[x], v; i; i = e[i].next) { v = e[i].to; if (v == fa || vis[v]) continue; get_md(v, x); md[x] = max(md[x], md[v] + 1); } } void calc(int x, int fa, int d) { g[d]++; for (int i = 1; i <= m; ++i) { if (Luck[i] >= d) sum += f[Luck[i] - d]; } for (int i = head[x], v; i; i = e[i].next) { v = e[i].to; if (v == fa || vis[v]) continue; calc(v, x, d + 1); } } void dfs(int x, int fa) { ans = inf; get_rt(x, fa); vis[rt] = 1; get_md(rt, 0); f[0] = 1; for (int i = head[rt], v; i; i = e[i].next) { v = e[i].to; if (vis[v]) continue; calc(v, rt, 1); for (register int j = 0; j <= md[rt]; ++j) { f[j] += g[j]; g[j] = 0; } } for (register int j = 0; j <= md[rt]; ++j) { f[j]=0; } for (int i = head[rt], v; i; i = e[i].next) { v = e[i].to; if (vis[v]) continue; nowN = Size[v]; dfs(v, rt); } } inline void add(int u, int v) { tot++; e[tot].to = v; e[tot].next = head[u]; head[u] = tot; } int main() { read(n); read(m); for (register int i = 1; i <= m; ++i)read(Luck[i]); for (register int i = 1, u, v; i < n; ++i) { read(u); read(v); add(u, v); add(v, u); } int a; nowN = n; dfs(1, 0); a = (n + 2) / 3, printf("%.2lf\n", 1.0 * a * (a - 1) * sum / (1.0 * n * (n - 1))); a = (n + 1) / 3, printf("%.2lf\n", 1.0 * a * (a - 1) * sum / (1.0 * n * (n - 1))); a = n / 3, printf("%.2lf\n", 1.0 * a * (a - 1) * sum / (1.0 * n * (n - 1))); return 0; }
来源:https://www.cnblogs.com/smallocean/p/11221692.html