• [学习笔记]点分治


    点分治

    解决问题

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

    基本思路

    • 分治思想

      对于某一点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
    ", 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
    ", 1.0 * a * (a - 1) * sum / (1.0 * n * (n - 1)));
        a = (n + 1) / 3, printf("%.2lf
    ", 1.0 * a * (a - 1) * sum / (1.0 * n * (n - 1)));
        a = n / 3, printf("%.2lf
    ", 1.0 * a * (a - 1) * sum / (1.0 * n * (n - 1)));
        return 0;
    }
    
    不要忘记努力,不要辜负自己 欢迎指正 QQ:1468580561
  • 相关阅读:
    LeetCode 150:逆波兰表达式求值 Evaluate Reverse Polish Notation
    LeetCode 20:有效的括号 Valid Parentheses
    LeetCode 155:最小栈 Min Stack
    rsync & sersync 实时同步
    rsync & inotify-tools 实时同步
    rsync 应用总结
    Linux 变量详解
    特殊权限
    Linux 三剑客之sed命令总结
    expect 分发ssh key脚本
  • 原文地址:https://www.cnblogs.com/smallocean/p/11221692.html
Copyright © 2020-2023  润新知