• Tree 点分治


    题意:给一颗树,n个点,n-1条边,问有多少对点满足两点之间的距离小于k。码一道题

    思路:设这个树的根为p,那么点对(x,y)分为过p点或者在p的子树部分,我们将点对按照所过的根节点进行划分成子问题处理,对于每个以p为根的子树,对于这个树,我们求d数组记录点到根的距离,b数组记录点所在根哪个子节点,之后用a数组记录节点,并把a数组按d排序,之后就可以通过双指针扫描求满足条件点对的个数。

    //Author:XuHt
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    const int N = 10006;
    int n, k, s[N], Ans;
    bool v[N], w[N];
    int Head[N], Edge[N<<1], Leng[N<<1], Next[N<<1], t;
    int ans, pos;
    int d[N], b[N], a[N], tot, cnt[N];
    
    void dfs_find(int S, int x) {
        v[x] = 1;
        s[x] = 1;
        int max_part = 0;
        for (int i = Head[x]; i; i = Next[i]) {
            int y = Edge[i];
            if (v[y] || w[y]) continue;
            dfs_find(S, y);
            s[x] += s[y];
            max_part = max(max_part, s[y]);
        }
        max_part = max(max_part, S - s[x]);
        if (max_part < ans) {
            ans = max_part;
            pos = x;
        }
    }
    
    void dfs(int x) {
        v[x] = 1;
        for (int i = Head[x]; i; i = Next[i]) {
            int y = Edge[i], z = Leng[i];
            if (v[y] || w[y]) continue;
            ++cnt[b[a[++tot]=y]=b[x]];
            d[y] = d[x] + z;
            dfs(y);
        }
    }
    
    bool cmp(int i, int j) {
        return d[i] < d[j];
    }
    
    void work(int S, int x) {
        memset(v, 0, sizeof(v));
        ans = S;
        dfs_find(S, x);
        memset(d, 0, sizeof(d));
        memset(cnt, 0, sizeof(cnt));
        memset(v, 0, sizeof(v));
        w[a[tot=1]=b[pos]=pos] = 1;
        ++cnt[pos];
        for (int i = Head[pos]; i; i = Next[i]) {
            int y = Edge[i], z = Leng[i];
            if (v[y] || w[y]) continue;
            ++cnt[a[++tot]=b[y]=y];
            d[y] = z;
            dfs(y);
        }
        sort(a + 1, a + tot + 1, cmp);
        int l = 1, r = tot;
        --cnt[b[a[1]]];
        while (l < r) {
            while (d[a[l]] + d[a[r]] > k) --cnt[b[a[r--]]];
            Ans += r - l - cnt[b[a[l]]];
            --cnt[b[a[++l]]];
        }
        int now = pos;
        for (int i = Head[now]; i; i = Next[i]) {
            int y = Edge[i];
            if (w[y]) continue;
            work(s[y], y);
        }
    }
    
    void add(int x, int y, int z) {
        Edge[++t] = y;
        Leng[t] = z;
        Next[t] = Head[x];
        Head[x] = t;
    }
    
    void Tree() {
        t = 0;
        memset(Head, 0, sizeof(Head));
        for (int i = 1; i < n; i++) {
            int x, y, z;
            scanf("%d %d %d", &x, &y, &z);
            add(x, y, z);
            add(y, x, z);
        }
        memset(w, 0, sizeof(w));
        Ans = 0;
        work(n, 1);
        cout << Ans << endl;
    }
    
    int main() {
        while (cin >> n >> k && n && k) Tree();
        return 0;
    }
  • 相关阅读:
    Net设计模式实例之命令模式(Command Pattern)
    Jquery实现回车键Enter切换焦点
    Asp.net MVC2.0系列文章显示列表和详细页面操作
    [入门篇]Jquery读取.Net WebService Json数据
    Net设计模式实例之中介者模式(Mediator Pattern)
    26个Jquery使用小技巧(jQuery tips, tricks & solutions)
    虚拟化让私有云逐步落地
    Jquery打造AdRotator轮转图片
    360度全方位沟通向上领导法则一
    使用Jquery+CSS如何创建流动导航菜单Fluid Navigation
  • 原文地址:https://www.cnblogs.com/dongdong25800/p/11419908.html
Copyright © 2020-2023  润新知