codeforces208E Blood Cousins

题目链接:codeforces208E

正解:$dsu$ $on$ $tree$

解题报告:

  又是一波$dsu$ $on$ $tree$咯…

  $p$级$cousin$其实就是对于$x$的$p$级祖先统计一下和$x$深度相同的点的个数,链剖$+dsu$ $on$ $tree$就好咯。

//It is made by ljh2000
//有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <ctime>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <string>
#include <complex>
#include <bitset>
using namespace std;
typedef long long LL;
typedef long double LB;
typedef complex<double> C;
const double pi = acos(-1);
const int MAXN = 100011;
const int MAXM = 200011;
int n,m,ecnt,first[MAXN],to[MAXM],next[MAXM],size[MAXN],son[MAXN];
int f[MAXN][18],father[MAXN],deep[MAXN],ans[MAXN],Son,cnt[MAXN];
struct node{ int id,h; }tmp;
vector<node>w[MAXN];

inline void link(int x,int y){ next[++ecnt]=first[x]; first[x]=ecnt; to[ecnt]=y; }
inline int find(int x){ if(father[x]!=x) father[x]=find(father[x]); return father[x]; }
inline void merge(int x,int y){ x=find(x); y=find(y); if(x!=y) father[y]=x; }
inline int getint(){
    int w=0,q=0; char c=getchar(); while((c<‘0‘||c>‘9‘) && c!=‘-‘) c=getchar();
    if(c==‘-‘) q=1,c=getchar(); while (c>=‘0‘&&c<=‘9‘) w=w*10+c-‘0‘,c=getchar(); return q?-w:w;
}

inline void dfs(int x,int fa){
	size[x]=1;
	for(int i=first[x];i;i=next[i]) {
		int v=to[i]; if(v==fa) continue;
		f[v][0]=x; deep[v]=deep[x]+1;
		dfs(v,x); size[x]+=size[v];
		if(size[v]>size[son[x]]) son[x]=v;
	}
}

inline int lca(int x,int t){
	for(int i=17;i>=0;i--)
		if(t&(1<<i))
			x=f[x][i],t^=(1<<i);
	return x;
}

inline void add(int x,int fa,int val){
	cnt[deep[x]]+=val;
	for(int i=first[x];i;i=next[i]) {
		int v=to[i]; if(v==fa || v==Son) continue;
		add(v,x,val);
	}
}

inline void solve(int x,int fa,bool top){
	for(int i=first[x];i;i=next[i]) {
		int v=to[i]; if(v==son[x] || v==fa) continue;
		solve(v,x,1);
	}

	if(son[x])
		solve(son[x],x,0),Son=son[x];

	add(x,fa,1); Son=0;
	for(int i=0,ss=w[x].size();i<ss;i++) {
		tmp=w[x][i];
		ans[tmp.id]=cnt[tmp.h]-1;
	}

	if(top)
		add(x,fa,-1);
}

inline void work(){
	n=getint(); for(int i=1;i<=n;i++) father[i]=i; int x,y;
	for(int i=1;i<=n;i++) {
		x=getint(); f[i][0]=x;
		if(x!=0) link(x,i);
		merge(x,i);
	}

	for(int i=1;i<=n;i++)
		if(f[i][0]==0)
			dfs(i,0);

	for(int j=1;j<=17;j++)
		for(int i=1;i<=n;i++)
			f[i][j]=f[f[i][j-1]][j-1];

	m=getint();
	for(int i=1;i<=m;i++) {
		x=getint(); y=getint();
		if(deep[x]<y)  { ans[i]=0; continue; }
		tmp.h=deep[x]; x=lca(x,y); if(x==0) { ans[i]=0; continue; }
		tmp.id=i; w[x].push_back(tmp);
	}

	for(int i=1;i<=n;i++)
		if(f[i][0]==0)
			solve(i,0,1);

	for(int i=1;i<=m;i++) printf("%d ",ans[i]);
}

int main()
{
    work();
    return 0;
}
//有志者,事竟成,破釜沉舟,百二秦关终属楚;苦心人,天不负,卧薪尝胆,三千越甲可吞吴。

  

时间: 2024-10-09 08:33:08

codeforces208E Blood Cousins的相关文章

Codeforces 246E Blood Cousins Return(Dsu On the Tree)

题目链接 Blood Cousins Return 1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define rep(i, a, b) for (int i(a); i <= (b); ++i) 6 7 const int N = 200010; 8 9 string s[N]; 10 int ans[N], sz[N], h[N]; 11 bool skip[N]; 12 vector <int> v[N

【CF208E】Blood Cousins

题目大意:给定一个 N 个点的森林,M 个询问,每次询问对于点 u 来说,有多少个点和 u 有相同的 K 级祖先. 题解:线段树合并适合处理子树贡献的问题. 发现要回答这个询问在点 u 处计算很困难,但是在 u 的 k 级祖先处处理询问很简单,即:问对于 v 子树中深度为 k 的节点的个数.因此,采用将询问离线,对于每个点的询问转化到其祖先节点,最后采用线段树合并即可. 代码如下 #include <bits/stdc++.h> #define mp make_pair #define pb

dsu on tree总结

dsu on tree 树上启发式合并.我并不知道为什么要叫做这个名字... 干什么的 可以在\(O(n\log n)\)的时间内完成对子树信息的询问,可横向对比把树按\(dfs\)序转成序列问题的\(O(n\sqrt n)\)莫队算法. 怎么实现 当\(dfs\)到一个点\(u\),执行以下操作: 1.递归处理所有轻儿子; 2.递归处理重儿子; 3.计算整棵子树的贡献(在第2步中重儿子的贡献得以保留,所以不需要重复计算); 4.若点\(u\)不是其父亲的重儿子,删除整棵子树的贡献. 看上去像是

Given a family tree, find out if two people are blood related

Given a family tree for a few generations for the entire population and two people write a routine that will find out if they are blood related. Siblings are blood related since they have the same parents. Cousins are blood related since one of their

HOJ 13813 Blood groups

Blood groups Time Limit: 2000ms, Special Time Limit:5000ms, Memory Limit:131072KB Total submit users: 2, Accepted users: 2 Problem 13813 : No special judgement Problem description There are four possible blood groups for humans: AB, A, B and O, meani

白细胞white blood cell(leukocyte)

维基百科 http://zh.wikipedia.org/wiki/%E7%99%BD%E8%A1%80%E7%90%83 白细胞(拉丁语:leucocytus(从古希腊语leukós "白"和kýtos "中空"),德语:Leukozyt, 英语:white blood cell或leukocyte),是血液中一种重要的血细胞.除白细胞外,人体血液中还含有红血球.血小板和血浆. 白细胞作为免疫系统的一部分帮助身体抵抗传染病以及外来的东西.白细胞有核,能作变形运动,

AOJ 739 First Blood

First Blood Time Limit: 1000 ms   Memory Limit: 64 MBTotal Submission: 152   Submission Accepted: 37 Description 盖伦是个小学一年级的学生,在一次数学课的时候,老师给他们出了一个难题:老师给了一个正整数 n,需要在不大于n的范围内选择三个正整数(可以是相同的),使它们三个的最小公倍数尽可能的大.盖伦很想第一个解决这个问题,你能帮助盖伦拿到“first blood”吗? Input 首先

2015安徽省赛 A.First Blood

题目描述 盖伦是个小学一年级的学生,在一次数学课的时候,老师给他们出了一个难题: 老师给了一个正整数 n,需要在不大于n的范围内选择三个正整数(可以是相同的),使它们三个的最小公倍数尽可能的大.盖伦很想第一个解决这个问题,你能帮助盖伦拿到“first blood”吗? 输入 首先是一个正整数T,表示有T组测试数据 每组测试数据是一个正整数n(1<=n<=10^6) 输出 对于每组测试数据,输出最大的最小公倍数,每个输出单独占一行 样例输入 2 9 7 样例输出 504 210 第一次写的版本相

first blood(15程序设计大赛)暴力搜索,剪枝是关键

First Blood 题目描述 盖伦是个小学一年级的学生,在一次数学课的时候,老师给他们出了一个难题: 老师给了一个正整数 n,需要在不大于n的范围内选择三个正整数(可以是相同的),使它们三个的最小公倍数尽可能的大.盖伦很想第一个解决这个问题,你能帮助盖伦拿到“first blood”吗? 输入 首先是一个正整数T,表示有T组测试数据 每组测试数据是一个正整数n(1<=n<=10^6) 输出 对于每组测试数据,输出最大的最小公倍数,每个输出单独占一行 样例输入 2 9 7 样例输出 504