CF208E Blood Cousins

题面

Blood Cousins - 洛谷

题目大意:

给出一个n个点的森林,m次询问,每次求一个点的k级亲戚的数量

k级亲戚定义为,u≠v,u,v的k级祖先相同,则u,v为k级亲戚

题解

一定仔细读题md,是k级祖先,不是LCA为k级祖先

我的做法是先倍增求点u的k级祖先

然后统计k级祖先下深度为dep[u]点的个数

想来想去没想到好方法,然后就dfs序+莫队莽过去了

后来发现可以线段树合并,dsu on tree,每个点开一个线段树,

然后把询问挂在点上,遍历一遍,一个一个合并线段树,合并完之后做查询记录答案。

再后来发现dfs序+主席树可以在线。

代码(莫队):

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int gi()
{
	int num=0,flg=1;char c;
	while((c=getchar())<'0'||c>'9')if(c=='-')flg=-1;
	while(c>='0'&&c<='9'){num=num*10+c-48;c=getchar();}
	return num*flg;
}
#define N 100005
#define LOG 17
#define D 310
int fir[N],to[2*N],nxt[2*N],cnt;
int fa[N][LOG+2];
int getk(int x,int k)
{
	for(int i=LOG;i>=0;i--)
		if(k&(1<<i)) x=fa[x][i];
	return x;
}
void adde(int a,int b)
{
	to[++cnt]=b;nxt[cnt]=fir[a];fir[a]=cnt;
}
int dfn[N],siz[N],dep[N],dcnt;
int num[N];
void dfs(int u,int d)
{
	dfn[u]=++dcnt;
	num[dcnt]=u;
	siz[u]=1;dep[u]=d;
	for(int v,p=fir[u];p;p=nxt[p]){
		v=to[p];
		if(!dfn[v]){
			dfs(v,d+1);
			siz[u]+=siz[v];
		}
	}
}
int qcnt;
struct node{
	int l,r,k,flg,id;
	bool operator < (const node &t)const{
		return l/D<t.l/D || (l/D==t.l/D && ((((l/D)&1) && r<t.r) || ( !((l/D)&1) && r>t.r)));
	}
}q[N];
int depcnt[N];
int ans[N];
int main()
{
	int n,m,i,j,x,v,k;
	n=gi();
	for(i=1;i<=n;i++){
		fa[i][0]=gi();
		adde(fa[i][0],i);
	}
	for(j=1;j<=LOG;j++)
		for(i=1;i<=n;i++)
			fa[i][j]=fa[fa[i][j-1]][j-1];
	for(i=1;i<=n;i++)
		if(!fa[i][0])dfs(i,0);
	m=gi();
	for(i=1;i<=m;i++){
		v=gi();k=gi();
		x=getk(v,k);
		if(x!=0){
			q[++qcnt].l=dfn[x];
			q[qcnt].r=dfn[x]+siz[x]-1;
			q[qcnt].k=dep[v];
			q[qcnt].flg=1;
			q[qcnt].id=i;
		}
	}
	sort(q+1,q+qcnt+1);
	int l=1,r=0;
	for(i=1;i<=qcnt;i++){
		while(r<q[i].r){
			r++;
			depcnt[dep[num[r]]]++;
		}
		while(l>q[i].l){
			l--;
			depcnt[dep[num[l]]]++;
		}
		while(l<q[i].l){
			depcnt[dep[num[l]]]--;
			l++;
		}
		while(r>q[i].r){
			depcnt[dep[num[r]]]--;
			r--;
		}
		ans[q[i].id]+=depcnt[q[i].k]*q[i].flg;
	}
	for(i=1;i<=m;i++){
		if(ans[i])
			printf("%d ",ans[i]-1);
		else
			printf("0 ");;
	}
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值