【POJ 1987】Distance Statistics【樹的分治】

題意:給出一棵樹,求兩點路徑之和小於等於k的點對數目。

思路:簡單的樹的點分治,每次找出樹的重心,然後分兩步求得ans,1,經過重心的路徑。2,不經過重心的路徑。

第二種情況,直接遞歸它的孩子的子樹求的即可。對於第一種情況,我們一次遍歷它的孩子子樹,記錄能形成距離,然後對於下一個孩子子樹遍歷時得到的距離,通過二分之前子樹的距離,就可以得到有多少對點滿足條件。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define N 40009
typedef long long ll;

struct E{
    int v, d, ne;
    E(){}
    E(int _v, int _d, int _ne):v(_v), d(_d), ne(_ne){}
}e[N*2];

int n, m, size, ca = 1, head[N];
int vis[N] = {0}, sun[N], root, mi;
ll ans, k;
vector<ll>V[N];

inline void init() {
    size = 0;
    memset(head, -1, sizeof(head));
}

void add(int u, int v, int d) {
    e[size] = E(v, d, head[u]);
    head[u] = size++;
}

void cal(int u, int f, int s) {
    int i, v, mx = 0;
    if (f == -1) mi = N;
    sun[u] = 1;
    for (i = head[u];~i;i = e[i].ne) {
        v = e[i].v;
        if (vis[v] == ca || f == v) continue;
        cal(v, u, s);
        sun[u] += sun[v];
        if (sun[v] > mx) mx = sun[v];
    }
    if  (mx < s-sun[u]) mx = s-sun[u];
    if (mx < mi) mi = mx, root = u;
}

void dfs(int u, int f, ll d, int id) {
    int i, v;
    sun[u] = 1;
    if (d <= k) ans++;
    V[id].push_back(d);
    for (i = head[u];~i;i = e[i].ne) {
        v = e[i].v;
        if (vis[v] == ca || v == f) continue;
        dfs(v, u, d+e[i].d, id);
        sun[u] += sun[v];
    }
}

void solve(int u, int sum) {
    int v, i, j, id = 0, h;
    cal(u, -1, sum);
    vis[root] = ca;
   // printf("[%d %d %d %lld]\n", u, sum, root, ans);
    for (i = head[root];~i;i = e[i].ne) {
        v = e[i].v;
        if (vis[v] == ca) continue;
        V[id].clear();
        dfs(v, root, e[i].d*1ll, id);
        sort(V[id].begin(), V[id].end());
        id++;
    }//printf("%lld\n", ans);
    for (i = 0;i < id;i++) {
        for (h = 0;h < V[i].size();h++) {
            if (V[i][h] > k) break;
            for (j = i+1;j < id;j++) {
                int pos = upper_bound(V[j].begin(), V[j].end(), k-V[i][h])-V[j].begin();
                ans += pos;
            }
        }
    }
    for (i = head[root];~i;i = e[i].ne) {
        v = e[i].v;
        if (vis[v] == ca) continue;
        solve(v, sun[v]);
    }
}

int main() {
    int u, v, i, j, d;
    char w;
    while (~scanf("%d%d", &n, &m)) {
        init();
        while (m--) {
            scanf("%d%d%d %c", &u, &v, &d, &w);
            add(u, v, d), add(v, u, d);
        }
        scanf("%lld", &k);
        ans = 0;
        for (i = 1;i <= n;i++) {
            if (vis[i] == ca) continue;
            cal(i, -1, n);
            solve(i, sun[i]);
        }
        ca++;
        printf("%lld\n", ans);
    }
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章