BZOJ2599 樹上的詢問(點分治)
阿新 • • 發佈:2018-12-15
題目描述:
一棵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;
}