计蒜客 A组模拟赛 青出于蓝胜于蓝(dfs序,树状数组)

题意:中文

武当派一共有 n 人,门派内 n 人按照武功高低进行排名,武功最高的人排名第 1,次高的人排名第 2,... 武功最低的人排名第 n。现在我们用武功的排名来给每个人标号,除了祖师爷,每个人都有一个师父,每个人可能有多个徒弟。

我们知道,武当派人才辈出,连祖师爷的武功都只能排行到 p。也就是说徒弟的武功是可能超过师父的,所谓的青出于蓝胜于蓝。

请你帮忙计算每个人的所有子弟(包括徒弟的徒弟,徒弟的徒弟的徒弟....)中,有多少人的武功超过了他自己。

思路:对于面对树形结构的题我们如果把他变成线性就好整了多了,那么怎样将他变成线性的呢,我们用dfs序,开两个数组,一个in一个out,分别表示的是它深搜时的编码和深搜结束后的编码,我们in[x]~out[x]表示的就是x点管辖的区间(也就是x点的子孙),之后的问题就很好解决了,就是求一下x管辖区间内的逆序数时多少吗

上代码把:

#include 
using namespace std;
const int maxn = 100000+10;
struct node
{
	int to,nex;
}edg[maxn];
vectorV[maxn];
int vis[maxn],in[maxn],out[maxn],ans[maxn];
int sum[maxn];
int n,p,a,b,cnt;
int lowbit(int x)
{
	return x&(-x);
}
void add(int x)
{
	//printf("X = %d\n",x);
	while(x<=n)
	{
		sum[x]++;
		x += lowbit(x);
	//	printf("X = %d\n",x);
	}
}
int query(int x)
{
	int ans = 0 ;
	while(x)
	{
		ans+=sum[x];
		x -= lowbit(x);
	}
	return ans;
}
void dfs(int u)
{
	if(vis[u]) return ;
	vis[u] = 1;
	in[u] = ++ cnt;
	for(int i = 0 ; i < V[u].size();i++)
	{
		int v = V[u][i];
		dfs(v);
	}
	out[u] = cnt;
}
int main()
{
	cin>>n>>p;
	memset(vis,0,sizeof(vis));
	cnt = 0;
	for(int i = 0 ; i < n-1;i++)
	{
		cin>>a>>b;
		V[a].push_back(b);
		V[b].push_back(a);
	}
	dfs(p);
	for(int i = n ; i >= 1 ; i--)
	{
		ans[i] = out[i] - in[i] -(query(out[i]) - query(in[i]));
		add(in[i]);
	}
	for(int i = 1 ; i <= n-1 ; i++)
	{
		printf("%d ",ans[i]);
	}
	printf("%d\n",ans[n]);
}


你可能感兴趣的:(线段树)