[湖南集训]谈笑风生
嘟嘟嘟
这题刚开始犹豫了一会儿,以为“高明”的优先级大于“谈笑风生”,不过样例表明只要两点间距离不超过\(x\),两人就算”谈笑风生“。
接下来看看怎么回答询问。
首先\(a\)是固定的,且\(a,b\)都是\(c\)的祖先。那就得分类讨论:
1.\(b\)是\(a\)的祖先,那么\(c\)就是\(a\)的子树中的所有点,根据乘法原理,三元组个数为\((size[a] - 1) * min(deep[a], x)\)。
2.\(a\)是\(b\)的祖先。那么对于\(a\)子树内的每一个\(b\),合法的\(c\)都有\(b\)的子树大小个,所以这种情况三元组个数为\(\sum _ {b \in a的子树,dis(a, b) \leqslant a} size[b] - 1\)。至于怎么求这些合法的\(b\),显然就是基于\(dfs\)序的主席树啦。
#include<cstdio>
#include<iostream>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<cctype>
#include<vector>
#include<stack>
#include<queue>
#include<assert.h>
using namespace std;
#define enter puts("")
#define space putchar(' ')
#define Mem(a, x) memset(a, x, sizeof(a))
#define In inline
typedef long long ll;
typedef double db;
const int INF = 0x3f3f3f3f;
const db eps = 1e-8;
const int maxn = 3e5 + 5;
In ll read()
{
ll ans = 0;
char ch = getchar(), last = ' ';
while(!isdigit(ch)) last = ch, ch = getchar();
while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar();
if(last == '-') ans = -ans;
return ans;
}
In void write(ll x)
{
if(x < 0) x = -x, putchar('-');
if(x >= 10) write(x / 10);
putchar(x % 10 + '0');
}
In void MYFILE()
{
#ifndef mrclr
freopen(".in", "r", stdin);
freopen(".out", "w", stdout);
#endif
}
int n, m;
struct Edge
{
int nxt, to;
}e[maxn << 1];
int head[maxn], ecnt = -1;
In void addEdge(int x, int y)
{
e[++ecnt] = (Edge){head[x], y};
head[x] = ecnt;
}
struct Tree
{
int ls, rs;
ll sum;
}t[maxn * 40];
int root[maxn], tcnt = 0;
In void insert(int old, int& now, int l, int r, int val, int d)
{
t[now = ++tcnt] = t[old];
t[now].sum += d;
if(l == r) return;
int mid = (l + r) >> 1;
if(val <= mid) insert(t[old].ls, t[now].ls, l, mid, val, d);
else insert(t[old].rs, t[now].rs, mid + 1, r, val, d);
}
In ll query(int old, int now, int l, int r, int id)
{
if(l == r) return t[now].sum - t[old].sum;
int mid = (l + r) >> 1;
if(id <= mid) return query(t[old].ls, t[now].ls, l, mid, id);
else return t[t[now].ls].sum - t[t[old].ls].sum + query(t[old].rs, t[now].rs, mid + 1, r, id);
}
int dep[maxn], siz[maxn], dfn[maxn], pos[maxn], cnt = 0;
In void dfs(int now, int _f)
{
siz[now] = 1;
dfn[now] = ++cnt, pos[cnt] = now;
for(int i = head[now], v; ~i; i = e[i].nxt)
{
if((v = e[i].to) == _f) continue;
dep[v] = dep[now] + 1;
dfs(v, now);
siz[now] += siz[v];
}
}
int main()
{
MYFILE();
Mem(head, -1);
n = read(), m = read();
for(int i = 1; i < n; ++i)
{
int x = read(), y = read();
addEdge(x, y), addEdge(y, x);
}
dfs(1, 0);
for(int i = 1; i <= cnt; ++i) insert(root[i - 1], root[i], 1, n, dep[pos[i]], siz[pos[i]] - 1);
for(int i = 1; i <= m; ++i)
{
int p = read(), K = read();
ll ans = 1LL * (siz[p] - 1) * min(dep[p], K);
ans += query(root[dfn[p]], root[dfn[p] + siz[p] - 1], 1, n, dep[p] + K);
write(ans), enter;
}
return 0;
}