hdu3015(树状数组)

http://acm.hdu.edu.cn/showproblem.php?pid=3015

题意:对于n棵树,给出所在位置和高度,然后分别对它的位置和高度做如下处理:

位置:将位置升序排序,最小的定义等级为 1,次小的定义等级为2,但是,要是位置相同的,则等级定义要相同;

例如:位置 1,2,1,5,2,3

        等级  1,3,1,6,3,5

对于高度也是做如上处理;

然后,定义f=两树之间的距离差的绝对值,s=两树中最小的高度,求所有树之间f*s和。

思路:先对输入的数据进行处理,定义好等级,之后,发现对s进行升序排序后,再对f来处理会比较好做。如果排完序后,用暴力做,肯定超时。再想想,处理一个数据前面或者后面有多少数比它大或者小的,可以用树状数组来处理.......对一个数a,前面有num个数比它小,后面有num1个数比它大,要求这个数a与这一串数中的每个数的差之和,可以num*a-a前面比a小的数之和+a后面比a大的数之和-num1*a

反思:想到用树状数组后,对测试数据计算完,没有错误就开始编题了,结果wa3次,耗时4小时才过。以后的题目,需要自己再想几组数据,计算完正确后,再提交.......

代码:

#include<iostream>
#include<algorithm>
using namespace std;
#define M 100005
struct node
{
	__int64 a,b;
	int num1,num2;
}t[M];
int cmpa(node &p,node &q)
{
	if(p.a<q.a)
		return 1;
	else
		return 0;
}
int cmpb(node &p,node &q)
{
	if(p.b<q.b)
		return 1;
	else
		return 0;
}
int cmpn(node &p,node &q)
{
	if(p.num2<q.num2)
		return 1;
	else
		return 0;
}
__int64 c[M],d[M],n;                    //c用来处理一个数前面或者后面比它大或者小的数之和,d用来处理这个数前面或者后面有多少个数
int lowbit(int x)                      //比它大或者小
{
	return x&(-x);
}
void updatac(int i,int j)
{
	while(i<=n)
	{
		c[i]+=j;
		i+=lowbit(i);
	}
}
void updatad(int i,int j)
{
	while(i<=n)
	{
		d[i]+=j;
		i+=lowbit(i);
	}
}
__int64 getsumc(int x)
{
	__int64 s=0;
	while(x>0)
	{
		s+=c[x];
		x-=lowbit(x);
	}
	return s;
}
__int64 getsumd(int x)
{
	__int64 s=0;
	while(x>0)
	{
		s+=d[x];
		x-=lowbit(x);
	}
	return s;
}
int main()
{
	while(scanf("%d",&n)>0)
	{
		memset(c,0,sizeof(c));
		memset(d,0,sizeof(d));
		int i;
		for(i=1;i<=n;i++)
		{
			scanf("%I64d%I64d",&t[i].a,&t[i].b);
		}
		sort(t+1,t+1+n,cmpa);
		for(i=1;i<=n;i++)
		{
			if(i!=1&&t[i].a==t[i-1].a)
				t[i].num1=t[i-1].num1;
			else
				t[i].num1=i;
		}
		int max=t[n].num1;
		//printf("%d\n",max);
		sort(t+1,t+1+n,cmpb);
		for(i=1;i<=n;i++)
		{
			if(i!=1&&t[i].b==t[i-1].b)
				t[i].num2=t[i-1].num2;
			else
				t[i].num2=i;
		}
		sort(t+1,t+1+n,cmpn);                //这之前都是对等级进行处理
		for(i=1;i<=n;i++)               //插入数据
		{
			updatac(t[i].num1,t[i].num1);
			updatad(t[i].num1,1);
			//printf("%d %d\n",t[i].num1,t[i].num2);
		}
		//printf("\n\n\n");
		__int64 sum=0,m=n,tmp1,tmp2;
		for(i=1;i<n;i++)
		{	
			//printf("%I64d\t %I64d\t %I64d\t %I64d\n",getsumc(max),getsumc(t[i].num1),n-getsumd(t[i].num1),t[i].num2);
			//sum+=(getsumc(max)-getsumc(t[i].num1)-(m-getsumd(t[i].num1))*t[i].num1)*t[i].num2;
			/*
			在这里,sum错了一次,一开始是想,算出所有数之和-小于等于t[i].num1的数之和,再减去t[i].num1这个数与其他数相减的次数*t[i].num1;
			这样做是错的,这组数据过不了:
			4 1
			1 2
			4 3
			1 4
			3 4
			正确结果是37.
			*/
			//printf("%I64d\t%I64d\n",getsumc(max),getsumc(100));
			tmp1=getsumd(t[i].num1-1)*t[i].num1-getsumc(t[i].num1-1);
			tmp2=(getsumd(max)-getsumd(t[i].num1))*t[i].num1-(getsumc(max)-getsumc(t[i].num1));
			/*
			之后,我再思考,发现可以先求出t[i].num后面比t[i].num小、大的数之和,还有比它小、大的数的个数,用比它小的数的个数*它本身-比
			它小的数之和,再求出比它大的数的个数*它本身-比它大的数.............;再这两个数相减....结果就出来了;但是,每求出一个数后,
			要把这个数删除,以免下面会重复求这个数.............
			*/
			sum+=(tmp1-tmp2)*t[i].num2;
			updatac(t[i].num1,-t[i].num1);
			updatad(t[i].num1,-1);
			m--;
		}
		printf("%I64d\n",sum);
	}
	return 0;
}

 

 

posted @ 2012-12-30 18:17  紫忆  阅读(707)  评论(0编辑  收藏  举报