现在的位置: 首页 > 综合 > 正文

hdu 3887 (树状数组)

2018年12月26日 ⁄ 综合 ⁄ 共 1562字 ⁄ 字号 评论关闭

题意:给出一棵树,给出树根,求出每个节点有多少个比自己小的子节点。

思路:刚开始读完题后就知道用先深搜把每个节点按照搜索的先后顺序形成一个序列,然后就是对这个序列求出每个点前面的一段区间比自己小的数的数量,每个区间也可以求出来。

样例形成的序列 :4 12 15 3 8 5 6 11 9 1 13 2 14 10 7,7的区间是4—7,14区间是13—14,,,,

就是求出每个区间的顺序数,这里可以用到求逆序数的求法,逆序数的一种求法是树状数组:当插入一个值时对于该值前面的所有数组都加1。这里我们可以向后加,但是这求得是序列的第一个元素到一点的顺序数,多了一部分,所有我们要在每个区间的前后各求一次,然后用后边的减去前边的就可以了,比如求14的时候在13时就求一次,然后在14再求一次。




#pragma comment(linker, "/STACK:1024000000,1024000000")    
#include<stdio.h>
#include<string.h>
const int N=100100;
int head[N],num,dfn[N],low[N],idx,dp[N],tot[N],n;
struct edge
{
	int ed,next;
}e[N*2];
void addedge(int x,int y)
{
	e[num].ed=y;e[num].next=head[x];head[x]=num++;
	e[num].ed=x;e[num].next=head[y];head[y]=num++;
}
void Addedge(int x,int y)
{
	e[num].ed=y;e[num].next=head[x];head[x]=num++;
}
void dfs(int u,int fa)
{
	int i,v,temp=9999999;	
	for(i=head[u];i!=-1;i=e[i].next)
	{
		v=e[i].ed;
		if(v==fa)continue;
		dfs(v,u);
		if(temp>low[v])
			temp=low[v];
	}
	dfn[++idx]=u;
	low[u]=idx;
	if(low[u]>temp)//该节点的子节点最小的标号
		low[u]=temp;
}
void plus(int i)
{
	while(i<=n)
	{
		tot[i]++;
		i+=(i&(-i));//值比i大的树状数组都要+1
	}
}
int findsum(int i)
{
	int sum=0;
	while(i>0)
	{
		sum+=tot[i];
		i-=(i&(-i));
	}
	return sum;
}
int main()
{
	int i,x,y,root,j,v;
	while(scanf("%d%d",&n,&root),n+root)
	{
		memset(head,-1,sizeof(head));
		num=0;idx=0;
		for(i=1;i<n;i++)
		{
			scanf("%d%d",&x,&y);
			addedge(x,y);
		}
		dfs(root,0);
		memset(head,-1,sizeof(head));
		memset(dp,0,sizeof(dp));
		memset(tot,0,sizeof(tot));
		num=0;
		for(i=1;i<=n;i++)
			Addedge(low[i],i);//i区间的左端点是low[i]
		for(i=1;i<=n;i++)
		{
			x=dfn[i];//当前的值
			for(j=head[i];j!=-1;j=e[j].next)
			{
				v=e[j].ed;//v的区间左端为x的
				dp[v]=findsum(v);
			}
		   dp[x]=findsum(x)-dp[x];//减去左端的值
		   plus(x);//插入x的值
		}
		for(i=1;i<n;i++)
			printf("%d ",dp[i]);
		printf("%d\n",dp[n]);
	}
	return 0;
}

抱歉!评论已关闭.