HDU-3333_Turing Tree(树状数组+离散化+离线操作)

点击这里你将拥有一颗图灵树

Turing Tree

Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 8212    Accepted Submission(s): 3016

 

Problem Description

After inventing Turing Tree, 3xian always felt boring when solving problems about intervals, because Turing Tree could easily have the solution. As well, wily 3xian made lots of new problems about intervals. So, today, this sick thing happens again...

Now given a sequence of N numbers A1, A2, ..., AN and a number of Queries(i, j) (1≤i≤j≤N). For each Query(i, j), you are to caculate the sum of distinct values in the subsequence Ai, Ai+1, ..., Aj.

 

Input

The first line is an integer T (1 ≤ T ≤ 10), indecating the number of testcases below.
For each case, the input format will be like this:
* Line 1: N (1 ≤ N ≤ 30,000).
* Line 2: N integers A1, A2, ..., AN (0 ≤ Ai ≤ 1,000,000,000).
* Line 3: Q (1 ≤ Q ≤ 100,000), the number of Queries.
* Next Q lines: each line contains 2 integers i, j representing a Query (1 ≤ i ≤ j ≤ N).

 

Output

For each Query, print the sum of distinct values of the specified subsequence in one line.

 

Sample Input

 

2

3

1 1 4

2

1 2

2 3

5

1 1 2 1 3

3

1 5

2 4

3 5

 

Sample Output

 

1

5

6

3

6

 

就是先给你一串数字,再给你一些区间,让你求每个区间上不同数字的和

这道题的数字范围及其之大,1e9,显然暴力的话会超时,已经看到身边的小伙伴被秒杀了哦+_+

不暴力那要怎么做呢?当然是离散化喽,就是找一个映射,可以用map也可以自己定义一个数组

还有就是这道题可以离线操作,那么什么是离线操作呢?

在线操作就是一步一处理,即你输一个要求,他就给你整出来一个结果,离线操作恰恰相反,即把你的要求全部储存起来先,然后到程序结束后再一次性全部吐出来,据说这样做省时间,但是你储存结果就会浪费一定的空间,这就比较难受了,不过没问题,能AC题目的方法就是好方法;

这道题要怎么操作呢?可以先将输入的问题按照他的右端点进行排序,然后一个个去遍历,我们这里用map来储存某个数字上次出现的地方,当这个数字又一次出现时,就将上一次的位置给抹去,然后在当前位置重新插入,这样就实现了数字的不重复,再就是补上树状数组的板子就ojbk了

好了,下面把代码奉上

只是卑微的我有一个小小的需求,就是在代码的第64 行有个小小的bug,为什么写while可以AC,写成for循环就WA了呢???

求大佬帮忙解释     Orz

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map> 
using namespace std;
#define ll long long
const ll mm=30005;

ll t,n,m;
ll c[mm];
ll a[mm];
ll ans[200005];
map<ll,ll> lp;//数字与位置标号的映射关系 
struct node{
	ll l,r;
	ll id;
}pp[200005];
bool cmp(node a,node b){
	return a.r<b.r;
}
//板子套上  
ll lbt(ll x){
	return x&-x;
}

void up(ll pos,ll k){
	while(pos<=n){
		c[pos]+=k;
		pos+=lbt(pos);
	}
}

ll sum(ll k){
	ll res=0;
	while(k>0){
		res+=c[k];
		k-=lbt(k);
	}
	return res;
}

void init(){
	memset(c,0,sizeof(c));
	lp.clear();
} 

int main()
{
	scanf("%d",&t);
	while(t--){
		init();
		scanf("%lld",&n);	
		for(ll i=1;i<=n;i++)
			scanf("%lld",&a[i]);
		scanf("%lld",&m);
		for(ll i=1;i<=m;i++){
			scanf("%lld%lld",&pp[i].l,&pp[i].r);
			pp[i].id=i;
		}
		sort(pp+1,pp+m+1,cmp);//按照 r 升序 
		ll j=1;
		for(ll i=1;i<=m;i++){
			//为何写成 for(int j=1;j<=pp[i]].r;j++)就WA了呢??? 。。。已了解 并修正
			while(j<=pp[i].r){
				if(lp[a[j]]==0)//没出现过,加上  
					up(j,a[j]);
				else {//重复数字 
					up(lp[a[j]],-a[j]);//去掉原来的先 
					up(j,a[j]);//再加上现在的 
				}
				lp[a[j]]=j;//位置更新  
				j++; 
			}
			ans[pp[i].id]=sum(pp[i].r)-sum(pp[i].l-1);//离线操作  
		}
		for(ll i=1;i<=m;i++)
			printf("%lld\n",ans[i]);
	}
	return 0;
}

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值