[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)\)满足:
-
\(a,b\)和 \(c\)为$ T$ 中三个不同的点,且 \(a\)为\(p\) 号节点;
-
\(a\)和\(b\) 都比$ c$不知道高明到哪里去了;
-
\(a\)和\(b\) 谈笑风生。这里谈笑风生中的常数为给定的 \(k\)。
Input
第一行含有两个正整数\(n\)和\(q\),分别代表有根树的点数与询问的个数。
接下来\(n-1\)行,每行描述一条树上的边。每行含有两个整数\(u\)和\(v\),代表在节点\(u\)和\(v\)之间有一条边。
接下来\(q\)行,每行描述一个操作。第\(i\)行含有两个整数,分别表示第\(i\)个询问的\(p\)和\(k\)。
\(1 \le P \le N\)
\(1 \le K \le N\)
\(N \le 300000\)
\(Q \le 300000\)
Output
输出 \(q\) 行,每行对应一个询问,代表询问的答案。
Sample Input
5 3
1 2
1 3
2 4
4 5
2 2
4 1
2 3
Sample Output
3
1
3
\(dfs\)做差,利用树状数组维护。
我们来考虑一下暴力的做法,我们只用找到所有距离\(p\)小于等于\(k\)的所有节点,答案加上节点的\(size-1\)。
时间复杂度\(O(n^2)\).
让我们来考虑优化。
实际上,我们只用求出距离一个点小于等于\(k\)的\(\sum size[i]-1\),在该节点上方的可以直接算出,我们只用求出所有在它子树内满足条件的点即可。
我们该如何快速的求出这些点呢?
我们发现只用离线\(dfs\)做差维护深度的和即可。
代码如下
#include <cstdio>
#include <cstring>
#include <map>
#include <queue>
#include <iostream>
#include <algorithm>
using namespace std;
#define LL long long
#define int long long
#define u64 unsigned long long
#define Raed Read
#define reg register
#define debug(x) cerr<<#x<<" = "<<x<<endl;
#define rep(a,b,c) for(reg int a=(b),a##_end_=(c); a<=a##_end_; ++a)
#define ret(a,b,c) for(reg int a=(b),a##_end_=(c); a<a##_end_; ++a)
#define drep(a,b,c) for(reg int a=(b),a##_end_=(c); a>=a##_end_; --a)
#define erep(i,G,x) for(reg int i=(G).Head[x]; i; i=(G).Nxt[i])
inline int Read() {
int res = 0, f = 1;
char c;
while (c = getchar(), c < 48 || c > 57)if (c == '-')f = 0;
do res = (res << 3) + (res << 1) + (c ^ 48);
while (c = getchar(), c >= 48 && c <= 57);
return f ? res : -res;
}
template<class T>inline bool Min(T &a, T const&b) {
return a > b ? a = b, 1 : 0;
}
template<class T>inline bool Max(T &a, T const&b) {
return a < b ? a = b, 1 : 0;
}
const int N = 3e5 + 5, M = 3e5 + 5;
const LL mod = 998244353;
const int dx[5] = {1, -1, 0, 0, 0}, dy[5] = {0, 0, 0, 1, -1};
const double eps = 1e-6;
bool MOP1;
int Ans[N], dep[N], Sz[N], Fa[N];
struct Link_list {
int Head[N], Tot, Nxt[M << 1], to[M << 1];
inline void AddEdgepair(int a, int b) {
to[++Tot] = b, Nxt[Tot] = Head[a], Head[a] = Tot;
to[++Tot] = a, Nxt[Tot] = Head[b], Head[b] = Tot;
}
} G;
struct BIT {
int C[N];
inline void Add(int x, int y) {
while (x < N)C[x] += y, x += x & -x;
}
inline int Sum(int x) {
int res = 0;
while (x)res += C[x], x -= x & -x;
return res;
}
} tr;
struct node {
int b, id;
};
vector<node>Q[N];
void dfs(int x, int pre) {
dep[x] = dep[pre] + 1, Sz[x] = 1, Fa[x] = pre;
ret(i, 0, Q[x].size()) {
int ID = Q[x][i].id, b = Q[x][i].b;
Ans[ID] -= tr.Sum(dep[x] + b) - tr.Sum(dep[x]);
}
erep(i, G, x) {
int y = G.to[i];
if (y == pre)continue;
dfs(y, x);
Sz[x] += Sz[y];
}
ret(i, 0, Q[x].size()) {
int ID = Q[x][i].id, b = Q[x][i].b;
Ans[ID] += min(dep[x] - 1, b) * (Sz[x] - 1);
Ans[ID] += tr.Sum(dep[x] + b) - tr.Sum(dep[x]);
}
tr.Add(dep[x], Sz[x] - 1);
}
bool MOP2;
inline void _main(void) {
int n = Raed(), q = Read();
ret(i, 1, n) {
int a = Raed(), b = Raed();
G.AddEdgepair(a, b);
}
rep(i, 1, q) {
int a = Read(), b = Read();
Q[a].push_back((node) {b, i});
}
dfs(1, 0);
rep(i, 1, q)printf("%lld\n", Ans[i]);
}
signed main() {
#define offline1
#ifdef offline
freopen("tree.in", "r", stdin);
freopen("tree.out", "w", stdout);
_main();
fclose(stdin); fclose(stdout);
#else
_main();
#endif
return 0;
}