3653: 谈笑风生

3653: 谈笑风生

Time Limit: 20 Sec   Memory Limit: 512 MB
Submit: 498   Solved: 185
[ 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]





维护dfs序的线段树

每次询问的b要么在a上面(每组的权值是size[a])要么在a下面(每组权值size[b])

然后距离都不超过k

第一种很好处理

第二种在线段树一段区间里面还需满足深度符合

也就是线段树需要两个关键字判断(dfs序与深度)

这样的询问每次是O(log^2)的


一开始也不知道为什么想bfs序?显然不可以

然后query函数里面没及时return 0


#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
using namespace std;

const int maxn = 4E5 + 30;
typedef long long LL;

int n,m,p,k,clo,dfi[maxn],dfo[maxn],siz[maxn],L[maxn];
LL c[maxn*20];
int ac[maxn*20],bc[maxn*20];

vector <int> v[maxn];

void build(int o,int l,int r,int pos,int Add,int de)
{
	if (l == r) {c[o] = Add; ac[o] = bc[o] = de; return;}
	int mid = (l+r) >> 1;
	if (pos <= mid) build(2*o,l,mid,pos,Add,de);
	else build(2*o+1,mid+1,r,pos,Add,de);
	c[o] = c[2*o] + c[2*o+1];
	ac[o] = min(ac[2*o],ac[2*o+1]);
	bc[o] = max(bc[2*o],bc[2*o+1]);
}

void dfs(int k)
{
	dfi[k] = ++clo; 
	for (int i = 0; i < v[k].size(); i++) {
		int to = v[k][i];
		if (L[to]) continue;
		L[to] = L[k] + 1;
		dfs(to);
		siz[k] += siz[to] + 1;
	}
	dfo[k] = clo;
	build(1,1,n,dfi[k],siz[k],L[k]);
}

LL query(int o,int l,int r,int ql,int qr,int qa,int qb)
{
	if (ql <= l && r <= qr && qa <= ac[o] && bc[o] <= qb) return c[o];
	if (bc[o] < qa || qb < ac[o]) return 0;
	int mid = (l+r) >> 1; LL ret = 0;
	if (ql <= mid) ret += query(2*o,l,mid,ql,qr,qa,qb);
	if (qr > mid) ret += query(2*o+1,mid+1,r,ql,qr,qa,qb);
	return ret;
}

int main()
{
	#ifdef YZY
		freopen("yzy.txt","r",stdin);
	#endif
	
	cin >> n >> m; 
	for (int i = 1; i < n; i++) {
		int x,y; scanf("%d%d",&x,&y);
		v[x].push_back(y); v[y].push_back(x);
	}
	clo = 0; L[1] = 1; dfs(1); 
	
	while (m--) {
		int p,k; LL ans; scanf("%d%d",&p,&k);
		if (L[p] - k >= 1) ans = 1LL*k*1LL*siz[p];
		else ans = 1LL*(L[p]-1)*1LL*siz[p];
		if (dfi[p] + 1 <= dfo[p]) ans += query(1,1,n,dfi[p]+1,dfo[p],L[p] + 1,L[p] + k);
		printf("%lld\n",ans);
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值