【bzoj3653】谈笑风生

3653: 谈笑风生

Time Limit: 20 Sec   Memory Limit: 512 MB
Submit: 866   Solved: 346
[ Submit][ Status][ Discuss]

Description

设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。


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

HINT


1<=P<=N

1<=K<=N

N<=300000

Q<=300000

Source

[ Submit][ Status][ Discuss]





名字就很暴力


还是好搞的吧


首先询问的答案肯定是祖先中满足的对数+后代中满足的对数


祖先肯定简单了,我们来讨论一下后代

对于一个询问(p,k)

后代的对数肯定是Σsiz[j] (j为p的后代,且j与p的树上距离不超过k)


那这要怎么搞呢?

我们考虑把树转换成dfs序列

那么这时候答案为1 ~ dfn[p] + siz[p] - 1中距离不超过k的点的siz和 减去1 ~ dfn[p]中距离不超过k的点的siz和


用主席树算就好


代码:

#include<cstdio>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;

typedef long long LL;

const int maxseg = 6000000;
const int maxn = 300100;

vector<int> e[maxn];
int n,m,tme,rt[maxn];
LL lc[maxseg],rc[maxseg],sum[maxseg],tot;
int siz[maxn],dfn[maxn],fa[maxn],pos[maxn];
LL v[maxn],dep[maxn];

inline LL getint()
{
	LL ret = 0,f = 1;
	char c = getchar();
	while (c < '0' || c > '9')
	{
		if (c == '-') f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
		ret = ret * 10 + c - '0',c = getchar();
	return ret * f;
}

inline void dfs(int u,int fa)
{
	siz[u]++;
	pos[dfn[u] = ++tme] = dep[u];
	for (int i = 0; i < e[u].size(); i++)
	{
		int v = e[u][i];
		if (v == fa) continue;
		dep[v] = dep[u] + 1;
		dfs(v,u);
		siz[u] += siz[v];
	}
	v[dfn[u]] = siz[u];
}

inline void maintain(int o)
{
	sum[o] = sum[lc[o]] + sum[rc[o]];
}

inline int insert(int ac,int l,int r,int pos,LL x)
{
	int o = ++tot;
	sum[o] = sum[ac]; lc[o] = lc[ac]; rc[o] = rc[ac];
	if (l == r)
	{
		sum[o] += x;
		return o;
	}
	int mid = l + r >> 1;
	if (pos <= mid) lc[o] = insert(lc[ac],l,mid,pos,x);
	if (mid < pos) rc[o] = insert(rc[ac],mid + 1,r,pos,x);
	maintain(o);
	return o;
}

inline LL query(int o,int l,int r,int al,int ar)
{
	if (al <= l && r <= ar) return sum[o];
	int mid = l + r >> 1;
	if (ar <= mid) return query(lc[o],l,mid,al,ar);
	if (mid < al) return query(rc[o],mid + 1,r,al,ar);
	if (al <= mid && mid < ar) return query(lc[o],l,mid,al,mid) + query(rc[o],mid + 1,r,mid + 1,ar);
}

int main()
{
	n = getint(); m = getint();
	for (int i = 1; i <= n - 1; i++)
	{
		int u = getint(),v = getint();
		e[u].push_back(v);
		e[v].push_back(u);
	}
	dep[1] = 1;
	dfs(1,0);
	for (int i = 1; i <= n; i++) rt[i] = insert(rt[i - 1],1,n,pos[i],v[i] - 1);
	for (int i = 1; i <= m; i++)
	{
		int u = getint() , k = getint();
		LL x1 = 0,x2 = 0;
		if (siz[u] > 1)
		{
			x1 = query(rt[dfn[u] + siz[u] - 1],1,n,dep[u],dep[u] + k);
			x2 = query(rt[dfn[u]],1,n,dep[u],dep[u] + k);
		}
		LL x3 = min(dep[u] - 1,k * 1ll) * (siz[u] - 1);
		printf("%lld\n",x1 - x2 + x3);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值