点分治

解决问题

对于某些限定条件的路径静态地进行统计的算法.

基本思路

  • 分治思想

    对于某一点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;
}