[BZOJ3653]谈笑风生
[BZOJ3653]谈笑风生
试题描述
设T 为一棵有根树,我们做如下的定义:
• 设a和b为T 中的两个不同节点。如果a是b的祖先,那么称“a比b不知道
高明到哪里去了”。
• 设a 和 b 为 T 中的两个不同节点。如果 a 与 b 在树上的距离不超过某个给定
常数x,那么称“a 与b 谈笑风生”。
给定一棵n个节点的有根树T,节点的编号为1 到 n,根节点为1号节点。你需
要回答q 个询问,询问给定两个整数p和k,问有多少个有序三元组(a;b;c)满足:
1. a、b和 c为 T 中三个不同的点,且 a为p 号节点;
2. a和b 都比 c不知道高明到哪里去了;
3. a和b 谈笑风生。这里谈笑风生中的常数为给定的 k。
• 设a和b为T 中的两个不同节点。如果a是b的祖先,那么称“a比b不知道
高明到哪里去了”。
• 设a 和 b 为 T 中的两个不同节点。如果 a 与 b 在树上的距离不超过某个给定
常数x,那么称“a 与b 谈笑风生”。
给定一棵n个节点的有根树T,节点的编号为1 到 n,根节点为1号节点。你需
要回答q 个询问,询问给定两个整数p和k,问有多少个有序三元组(a;b;c)满足:
1. a、b和 c为 T 中三个不同的点,且 a为p 号节点;
2. a和b 都比 c不知道高明到哪里去了;
3. a和b 谈笑风生。这里谈笑风生中的常数为给定的 k。
输入
输入文件的第一行含有两个正整数n和q,分别代表有根树的点数与询问的个数。接下来n - 1行,每行描述一条树上的边。每行含有两个整数u和v,代表在节点u和v之间有一条边。
接下来q行,每行描述一个操作。第i行含有两个整数,分别表示第i个询问的p和k。
接下来q行,每行描述一个操作。第i行含有两个整数,分别表示第i个询问的p和k。
输出
输出 q 行,每行对应一个询问,代表询问的答案。
输入示例
5 3 1 2 1 3 2 4 4 5 2 2 4 1 2 3
输出示例
3 1 3
数据规模及约定
1<=P<=N
1<=K<=N
N<=300000
Q<=300000
题解
我们发现询问的答案分为两部分:b 是 a 的祖先;a 是 b 的祖先。
对于 b 是 a 的祖先的情况,这一部分的答案直接就是 min{ (dep[b] - 1), k } * (siz[a] - 1),就是先确定 b 在哪(可以在 a 到根节点的路径上,但要保证 a 与 b 距离不超过 k),再确定 c 在哪(可以发现 a 子树中除 a 外的所有节点都可以取到)。
对于 a 是 b 的祖先的情况,我们发现 b 的深度范围在区间 ( dep[a], dep[a] + k ] 内,每个 b 所对应的 c 都可以是 b 的子树除 b 外的所有节点;于是我们可以使用 dfs 序 + 主席树维护这个东西。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;
const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {
if(Head == Tail) {
int l = fread(buffer, 1, BufferSize, stdin);
Tail = (Head = buffer) + l;
}
return *Head++;
}
int read() {
int x = 0, f = 1; char c = Getchar();
while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); }
while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); }
return x * f;
}
#define maxn 300010
#define maxm 600010
#define maxnode 6000010
#define LL long long
int n, m, head[maxn], nxt[maxm], to[maxm];
void AddEdge(int a, int b) {
to[++m] = b; nxt[m] = head[a]; head[a] = m;
swap(a, b);
to[++m] = b; nxt[m] = head[a]; head[a] = m;
return ;
}
int siz[maxn], dep[maxn], dl[maxn], dr[maxn], uid[maxn], clo;
void build(int u, int fa) {
dl[u] = ++clo; uid[clo] = u;
siz[u] = 1;
for(int e = head[u]; e; e = nxt[e]) if(to[e] != fa) {
dep[to[e]] = dep[u] + 1;
build(to[e], u);
siz[u] += siz[to[e]];
}
dr[u] = clo;
return ;
}
int rt[maxn], ToT, lc[maxnode], rc[maxnode];
LL sumv[maxnode];
void update(int& y, int x, int l, int r, int p, int v) {
sumv[y = ++ToT] = sumv[x] + v;
if(l == r) return ;
int mid = l + r >> 1; lc[y] = lc[x]; rc[y] = rc[x];
if(p <= mid) update(lc[y], lc[x], l, mid, p, v);
else update(rc[y], rc[x], mid + 1, r, p, v);
return ;
}
LL query(int o, int l, int r, int ql, int qr) {
if(!o) return 0;
if(ql <= l && r <= qr) return sumv[o];
int mid = l + r >> 1; LL ans = 0;
if(ql <= mid) ans += query(lc[o], l, mid, ql, qr);
if(qr > mid) ans += query(rc[o], mid + 1, r, ql, qr);
return ans;
}
int main() {
n = read(); int q = read();
for(int i = 1; i < n; i++) {
int a = read(), b = read();
AddEdge(a, b);
}
dep[1] = 1; build(1, 0);
for(int i = 1; i <= clo; i++) update(rt[i], rt[i-1], 1, n, dep[uid[i]], siz[uid[i]] - 1);
while(q--) {
int u = read(), k = read();
LL ans = (LL)(siz[u] - 1) * min(k, dep[u] - 1);
ans += query(rt[dr[u]], 1, n, dep[u] + 1, dep[u] + k) - query(rt[dl[u]-1], 1, n, dep[u] + 1, dep[u] + k);
printf("%lld\n", ans);
}
return 0;
}

浙公网安备 33010602011771号