Turing Tree HDU - 3333 (线段树+离线处理)

题目链接:https://cn.vjudge.net/problem/HDU-3333

题意:区间查询i-j内不同数之和

思路:查找不同数不可能每次查询到叶子节点,必然会t,那么就离线处理:

1.将n个数的值及其下标保存,按下标排序。

2.将q个查询及其下标保存,按查询的右端点排序。

3.从前往后对于每个查询,插入下标<=它的元素,每次插入前判断一下,这个元素的值是否已经存在于线段树中(利用map判断,map中存储每个值对应元素的位置),若它已经存在,将原来的更新为0,保留现在的。(贪心思想,每个区间查询,两个值相同的元素只会被算一次,离r越近的元素越有可能被算到)。然后进行区间查询,按查询的下标保存结果。

4.按查询下标从小到大输出结果。

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
using namespace std;
typedef pair<int,int> pa;
typedef long long ll;  //注意所有元素加起来可能会爆int,要用long long存
const int N =3e4+10;
ll sum[N<<2],ans[100005];  //注意查询的个数比N要大,第一次这个ans数组开小了,还wa了一发...
pa a[N];
struct node
{
	int l,r,pos;
}q[100005];
void pushup(int x)
{
	sum[x]=sum[x<<1]+sum[x<<1|1];
}
void build(int x,int l,int r)
{
	sum[x]=0;
	if(l==r)
		return;
	int mid=l+r>>1;
	build(x<<1,l,mid);
	build(x<<1|1,mid+1,r);
	
}
void update(int x,int l,int r,int tem,int val)
{
	if(l==r)
	{
		sum[x]=val;return;
	}
	int mid=l+r>>1;
	if(tem<=mid)
		update(x<<1,l,mid,tem,val);
	else
		update(x<<1|1,mid+1,r,tem,val);
	pushup(x);
}
ll query(int x,int L ,int R,int l,int r)
{
	if(L<=l && r<=R)
		return sum[x];
	int mid=l+r>>1;
	ll ans=0;
	if(L<=mid)
		ans+=query(x<<1,L,R,l,mid);
	if(R>mid)
		ans+=query(x<<1|1,L,R,mid+1,r);
	return ans;
}
bool cmp(node x,node y)
{
	return x.r<y.r;
}
int main()
{
	int t,n,m;
	cin>>t;
	while(t--)
	{
		cin>>n;
		build(1,1,n);
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&a[i].second);
			a[i].first=i;
		}
		sort(a+1,a+n+1);
		cin>>m;
		for(int i=0;i<m;i++)
		{
			int l,r;
			scanf("%d%d",&l,&r);
			q[i]={l,r,i+1};
		}
		sort(q,q+m,cmp);
		map<int,int> mp;
		for(int i=0,j=1;i<m;i++)
		{
			int l=q[i].l,r=q[i].r;
			while(a[j].first<=q[i].r && j<=n)
			{
				int x=a[j].first,val=a[j].second;
				if(mp[val])
				{
					update(1,1,n,mp[val],0);  //将重复元素下标较小的清空
					mp[val]=x;
					update(1,1,n,x,val);
				}
				else
				{
					mp[val]=x;
					update(1,1,n,x,val);
				}
				j++;
			}
			ans[q[i].pos]=query(1,l,r,1,n);
		}
		for(int i=1;i<=m;i++)
			printf("%lld\n",ans[i]);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值