1. 程式人生 > >BZOJ2599 樹上的詢問(點分治)

BZOJ2599 樹上的詢問(點分治)

題目描述:

一棵n個點的帶權有根樹,有p個詢問,每次詢問樹中是否存在一條長度為Len的路徑,如果是,輸出Yes否輸出No.

輸入描述:

第一行兩個整數n, p分別表示點的個數和詢問的個數. 接下來n-1行每行三個數x, y, c,表示有一條樹邊x→y,長度為c. 接下來p行每行一個數Len,表示詢問樹中是否存在一條長度為Len的路徑.

輸出描述:

輸出有p行,Yes或No.

樣例輸入:

6 4 
1 2 5 
1 3 7 
1 4 1 
3 5 2 
3 6 3 
1 
8 
13 
14

樣例輸出:

Yes 
Yes 
No 
Yes

提示:

30%的資料,n≤100. 100%的資料,n≤10000,p≤100,長度≤1000000.

思路

利用點分治,首先把樹的重心找出來之後,然後以這一點為根,遞迴的解決,點分治處理的是經過根節點的路徑,所以在計算完當前節點之後要去重(兒子),每次把處理的所有深度排序,然後遍歷每一個深度值,看一下存在k-x的有多少個,利用二分實現。先把所有的查詢存下來,然後離線處理。

程式碼

#include <bits/stdc++.h>
using namespace std;
#define mem(a, b) memset(a, b, sizeof(a))
const int N = 1e4 + 10;
int first[N], tot, n, p;
int q[N], siz[N]
, f[N], d[N], deep[N], vis[N]; int sum, root; struct edge { int v, w, next; } e[N * 2]; void add_edge(int u, int v, int w) { e[tot].v = v, e[tot].w = w; e[tot].next = first[u]; first[u] = tot++; } void init() { mem(first, -1); tot = 0; sum = f[0] = n; root = 0; } void getroot
(int u, int fa) { siz[u] = 1, f[u] = 0; for (int i = first[u]; ~i; i = e[i].next) { int v = e[i].v; if (!vis[v] && v != fa) { getroot(v, u); siz[u] += siz[v]; f[u] = max(f[u], siz[v]); } } f[u] = max(f[u], sum - siz[u]); if (f[u] < f[root]) root = u; } void getdeep(int u, int fa) { deep[++deep[0]] = d[u]; for (int i = first[u]; ~i; i = e[i].next) { int v = e[i].v, w = e[i].w; if (!vis[v] && v != fa) { d[v] = d[u] + w; getdeep(v, u); } } } int findl(int l, int r, int k) { int ans = 0; while (l <= r) { int mid = (l + r) >> 1; if (deep[mid] == k) { ans = mid; r = mid - 1; } else if (deep[mid] < k) l = mid + 1; else r = mid - 1; } return ans; } int findr(int l, int r, int k) { int ans = -1; while (l <= r) { int mid = (l + r) >> 1; if (deep[mid] == k) { ans = mid; l = mid + 1; } else if (deep[mid] < k) l = mid + 1; else r = mid - 1; } return ans; } int cal(int u, int cost, int k) { d[u] = cost; deep[0] = 0; getdeep(u, 0); sort(deep + 1, deep + deep[0] + 1); int t = 0; for (int i = 1; i <= deep[0]; i++) { if (deep[i] + deep[i] > k) break; int l = findl(i, deep[0], k - deep[i]); int r = findr(i, deep[0], k - deep[i]); t += r - l + 1; } return t; } int ans[110]; void solve(int u) { for (int i = 1; i <= p; i++) ans[i] += cal(u, 0, q[i]); vis[u] = 1; for (int i = first[u]; ~i; i = e[i].next) { int v = e[i].v, w = e[i].w; if (!vis[v]) { for (int j = 1; j <= p; j++) ans[j] -= cal(v, w, q[j]); sum = siz[v]; root = 0; getroot(v, 0); solve(root); } } } int main() { // freopen("in.txt", "r", stdin); int u, v, w; scanf("%d%d", &n, &p); init(); for (int i = 1; i <= n - 1; i++) { scanf("%d%d%d", &u, &v, &w); add_edge(u, v, w); add_edge(v, u, w); } for (int i = 1; i <= p; i++) scanf("%d", &q[i]); getroot(1, 0); solve(root); for (int i = 1; i <= p; i++) puts(ans[i] ? "Yes" : "No"); return 0; }