点分治
解决问题
对于某些限定条件的路径静态地进行统计的算法.
基本思路
-
分治思想
对于某一点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 ***xn];
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;
}