BZOJ3653谈笑风生 离线+树状数组+DFS序

Posted CQzhangyu

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了BZOJ3653谈笑风生 离线+树状数组+DFS序相关的知识,希望对你有一定的参考价值。

【BZOJ3653】谈笑风生

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。
1<=P<=N
1<=K<=N
N<=300000
Q<=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

题解:如果确定了a和b,那么c的个数就是a和b的公共子树大小-1,所以我们考虑所有b的贡献。

先考虑b不在a子树中的情况,那么b只能在a到根的路径上,这种情况显然可以直接计算。

那么考虑b在a子树中的情况,相当于b的深度和DFS序都要在一个范围内,这可以看成一个二维数点问题,用离线+树状数组轻松解决。

 

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
const int maxn=300010;
typedef long long ll;
int n,m,cnt;
int to[maxn<<1],next[maxn<<1],head[maxn],dep[maxn],siz[maxn],fa[maxn],p1[maxn],p2[maxn],p[maxn];
ll s[maxn],ans[maxn];
struct node
{
	int x,k,org;
}q[maxn];
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)	f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+(gc^‘0‘),gc=getchar();
	return ret*f;
}
inline void add(int a,int b)
{
	to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
}
void dfs(int x)
{
	siz[x]=1,p1[x]=++p2[0];
	for(int i=head[x];i!=-1;i=next[i])	if(to[i]!=fa[x])
		fa[to[i]]=x,dep[to[i]]=dep[x]+1,dfs(to[i]),siz[x]+=siz[to[i]];
	p2[x]=p2[0];
}
bool cmpq(const node &a,const node &b)
{
	return a.k<b.k;
}
bool cmpp(const int &a,const int &b)
{
	return dep[a]<dep[b];
}
inline void updata(int x,int v)
{
	for(int i=x;i<=n;i+=i&-i)	s[i]+=v;
}
inline ll query(int x)
{
	ll ret=0;
	for(int i=x;i;i-=i&-i)	ret+=s[i];
	return ret;
}
int main()
{
	n=rd(),m=rd();
	int i,j,a,b;
	memset(head,-1,sizeof(head));
	for(i=1;i<n;i++)	a=rd(),b=rd(),add(a,b),add(b,a);
	dep[1]=1,dfs(1);
	for(i=1;i<=m;i++)
		q[i].x=rd(),q[i].k=rd(),ans[i]=(ll)(min(dep[q[i].x]-1,q[i].k)-1)*(siz[q[i].x]-1),q[i].k+=dep[q[i].x],q[i].org=i;
	sort(q+1,q+m+1,cmpq);
	for(i=1;i<=n;i++)	p[i]=i;
	sort(p+1,p+n+1,cmpp);
	for(i=j=1;i<=m;i++)
	{
		for(;dep[p[j]]<=q[i].k&&j<=n;j++)	updata(p1[p[j]],siz[p[j]]-1);
		ans[q[i].org]+=query(p2[q[i].x])-query(p1[q[i].x]-1);
	}
	for(i=1;i<=m;i++)	printf("%lld\n",ans[i]);
	return 0;
}

 

以上是关于BZOJ3653谈笑风生 离线+树状数组+DFS序的主要内容,如果未能解决你的问题,请参考以下文章

BZOJ_3653_谈笑风生_树状数组

bzoj 3653 谈笑风生——主席树

BZOJ3653: 谈笑风生

3653: 谈笑风生

bzoj 3653 谈笑风生 —— 主席树

CCPC河南省赛B-树上逆序对| 离线处理|树状数组 + 线段树维护逆序对 + dfs序 + 离散化