剑指Offer(中等)

linux root密码忘记怎么办?进入但用户模式修改密码

  返回  

(HDU)Turing Tree(离线+线段树)

2021/8/21 17:45:05 浏览:

题目链接:https://acm.hdu.edu.cn/showproblem.php?pid=3333

题目大意:

现在给定一个由 N 个数字 A1, A2, ..., AN 和一些 Queries(i, j) (1≤i≤j≤N) 组成的序列。对于每个 Query(i, j),您要计算子序列 Ai, Ai+1, ..., Aj 中不同值的总和。

输入

第一行是一个整数 T (1 ≤ T ≤ 10),表示下面的测试用例数。
对于每种情况,输入格式如下:
* 第 1 行:N (1 ≤ N ≤ 30,000)。
* 第 2 行:N 个整数 A1、A2、...、AN(0 ≤ Ai ≤ 1,000,000,000)。
* 第 3 行:Q(1 ≤ Q ≤ 100,000),查询的数量。
* 接下来的 Q 行:每行包含 2 个整数 i,j 代表一个查询(1 ≤ i ≤ j ≤ N)。

输出

对于每个查询,在一行中打印指定子序列的不同值的总和。

分析:看到这道题后我的第一想法就是暴力解决,但看到数据范围后我就放弃了,这道题是用离线解决的,如果当前的点对应的值在之前出现过就把这个值的出现位置设置为当前点,这样就保证了数轴上所有的点的值都不相同,然后我们对查询区间排下序,最后用双指针就能解决了,这种题目第一次遇到肯定不是很好理解,可以画个图来模拟一下,这样就会理解的更加清楚,下面是代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<map>
#include<queue>
using namespace std;
#define int long long
const int N=3e5+10;
int a[N],l[N],r[N];
int sum[N],ans[N];
map<int,int>pre;
struct node{
	int l,r,pos;
}p[N];
bool cmp(node a,node b)
{
	return a.r<b.r;
}
void pushup(int id)
{
	sum[id]=sum[id<<1]+sum[id<<1|1];
}

void build(int id,int L,int R)
{
	l[id]=L;r[id]=R;sum[id]=0;
	if(L==R) return ;
	int mid=L+R>>1;
	build(id<<1,L,mid);
	build(id<<1|1,mid+1,R);
	pushup(id);
}

void update_point(int x,int id,int val)
{
	if(l[id]==r[id])
	{
		sum[id]=val;
		return ;
	}
	int mid=l[id]+r[id]>>1;
	if(x<=mid) update_point(x,id<<1,val);
	else update_point(x,id<<1|1,val);
	pushup(id);
}

int query_interval(int id,int L,int R)
{
	if(l[id]>R||r[id]<L) return 0;
	if(l[id]>=L&&r[id]<=R) return sum[id];
	return query_interval(id<<1,L,R)+query_interval(id<<1|1,L,R);
}

signed main()
{
	int T;
	cin>>T;
	while(T--)
	{
		int n,q;
		pre.clear();//多组输入不要忘记清空 
		scanf("%lld",&n);
		build(1,1,n);
		for(int i=1;i<=n;i++)
			scanf("%lld",&a[i]);
		scanf("%lld",&q);
		for(int i=1;i<=q;i++)
		{
			scanf("%lld%lld",&p[i].l,&p[i].r);
			p[i].pos=i;
		}
		sort(p+1,p+q+1,cmp);
		int j=1;
		for(int i=1;i<=n;i++)
		{
			if(pre[a[i]]) update_point(pre[a[i]],1,0);//如果这个值在之前出现过,就把之前的标记清除 
			pre[a[i]]=i; 
			update_point(i,1,a[i]);//在这个点上打上标记 
			while(j<=q&&i==p[j].r)
			{
				ans[p[j].pos]=query_interval(1,p[j].l,p[j].r);
				j++;
			}
		}
		for(int i=1;i<=q;i++)
			printf("%lld\n",ans[i]);
	}
	return 0;
}

联系我们

如果您对我们的服务有兴趣,请及时和我们联系!

服务热线:18288888888
座机:18288888888
传真:
邮箱:888888@qq.com
地址:郑州市文化路红专路93号