hdu 3333 Turing Tree(线段树+离散化)

刚看到是3xian大牛的题就让我菊花一紧,觉着这题肯定各种高端大气上档次,结果果然没让我失望。

刚开始我以为是一个普通的线段树区间求和,然后啪啪啪代码敲完测试没通过,才注意到这个求和是要去掉相同的值的。

前两天我在做一道题的时候,也是因为数据范围太大(同样是1000000000)不能打表,当时想了许久,想到把这些数值存进一个数组里面,对这个数组进行排序,然后对于其中的某个值可以进行二分查找。今天我又长姿势了,原来这种方法叫做离散化。

刚开始我想着可以把那些相同的值用0去替代,但是一个解决不了的问题是如果把两个相同的值其中一个改为0,那么如果问题区间刚好包含这个值不包含另一个值该怎么处理,甚至如果问题区间问的就是这个值,那该怎么办。

苦思良久也没想到什么好办法。看了下别人的思路,是将所有的问题区间放在一起,以区间的右边界为关键字进行排序,然后对于线段树的值边插入边更新,具体思路见代码。

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define N 100005
#define M 30005
struct node
{
	int x,y;
	int id;
}Q[N];
struct tree
{
	int x,y;
	__int64 sum;
}a[M*3];
int cmp(const void *a,const void *b)
{
	return *(int *)a-*(int *)b;
}
int cmp1(const void *a,const void *b)
{
	node *c;
	node *d;
	c=(node *)a;
	d=(node *)b;
	return c->y-d->y;
}
int visit[M],temp[M],tem[M],b[M];
__int64 ans[N];
void CreatTree(int t,int x,int y)
{
	a[t].x=x;
	a[t].y=y;
	a[t].sum=0;
	if(x==y)
		return ;
	int temp=t*2;
	int mid=(x+y)/2;
	CreatTree(temp,x,mid);
	CreatTree(temp+1,mid+1,y);
	return ;
}
void InsertTree(int t,int x,int y)
{
	if(a[t].x==a[t].y)
	{
		a[t].sum+=y;
		return ;
	}
	int temp=t*2;
	int mid=(a[t].x+a[t].y)/2;
	if(x<=mid)
		InsertTree(temp,x,y);
	else
		InsertTree(temp+1,x,y);
	a[t].sum=a[temp].sum+a[temp+1].sum;
	return ;
}
__int64 FindTree(int t,int x,int y)
{
	if(a[t].x==x&&a[t].y==y)
		return a[t].sum;
	int temp=t*2;
	int mid=(a[t].x+a[t].y)/2;
	__int64 sum;
	sum=0;
	if(y<=mid)
		sum+=FindTree(temp,x,y);
	else if(x>mid)
		sum+=FindTree(temp+1,x,y);
	else
	{
		sum+=FindTree(temp,x,mid);
		sum+=FindTree(temp+1,mid+1,y);
	}
	return sum;
}
int Find(int x,int k)
{
	int l,r,mid;
	l=1;
	r=k;
	while(l<=r)
	{
		mid=(l+r)/2;
		if(temp[mid]>x)
			r=mid-1;
		else if(temp[mid]<x)
			l=mid+1;
		else
			return mid;
	}
	return 0;
}
int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
		int n;
		scanf("%d",&n);
		CreatTree(1,1,n);
		int i,j,k;
		j=0;
		for(i=1;i<=n;i++)
		{
			scanf("%d",&b[i]);
			tem[j++]=b[i];
		}
		qsort(tem,j,sizeof(tem[0]),cmp);
		k=0;
		temp[0]=tem[0];
		for(i=1;i<j;i++)
		{
			if(tem[i]!=temp[k])
			{
				k++;
				temp[k]=tem[i];
			}
		}
		memset(visit,0,sizeof(visit));
		int m;
		scanf("%d",&m);
		for(i=1;i<=m;i++)
		{
			scanf("%d%d",&Q[i].x,&Q[i].y);
			Q[i].id=i;
		}
		qsort(Q+1,m,sizeof(Q[0]),cmp1);
		j=1;
		for(i=1;i<=n;i++)
		{
			int flag;
			int id;
			id=Find(b[i],k);
			flag=visit[id];
			if(flag)
				InsertTree(1,flag,-b[i]);
			InsertTree(1,i,b[i]);
			visit[id]=i;
			for(;j<=m;j++)
			{
				if(i==Q[j].y)
				{
					__int64 tt;
					tt=FindTree(1,Q[j].x,Q[j].y);
					ans[Q[j].id]=tt;
				}
				else
					break;
			}
		}
		for(i=1;i<=m;i++)
			printf("%I64d\n",ans[i]);
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值