[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\)

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;
}
posted @ 2019-07-29 12:55  dsjkafdsaf  阅读(204)  评论(0编辑  收藏  举报