您的位置:首页 > 其它

[树状数组&线段树]HDU3874 Necklace 离线思想

2015-07-24 16:15 381 查看
Problem Description

Mery has a beautiful necklace. The necklace is made up of N magic balls. Each ball has a beautiful value. The balls with the same beautiful value look the same, so if two or more balls have the same beautiful value, we just count it once. We define the beautiful value of some interval [x,y] as F(x,y). F(x,y) is calculated as the sum of the beautiful value from the xth ball to the yth ball and the same value is ONLY COUNTED ONCE. For example, if the necklace is 1 1 1 2 3 1, we have F(1,3)=1, F(2,4)=3, F(2,6)=6.

Now Mery thinks the necklace is too long. She plans to take some continuous part of the necklace to build a new one. She wants to know each of the beautiful value of M continuous parts of the necklace. She will give you M intervals [L,R] (1<=L<=R<=N) and you must tell her F(L,R) of them.

Input

The first line is T(T<=10), representing the number of test cases.

For each case, the first line is a number N,1 <=N <=50000, indicating the number of the magic balls. The second line contains N non-negative integer numbers not greater 1000000, representing the beautiful value of the N balls. The third line has a number M, 1 <=M <=200000, meaning the nunber of the queries. Each of the next M lines contains L and R, the query.

Output

For each query, output a line contains an integer number, representing the result of the query.

Sample Input

2

6

1 2 3 4 3 5

3

1 2

3 5

2 6

6

1 1 1 2 3 5

3

1 1

2 4

3 5

Sample Output

3

7

14

1

3

6

Source

2011 Multi-University Training Contest 4 - Host by SDU

Recommend

lcy

本题给定一数的序列,然后给定若干个查询区间,要求区间内不重复的数的和。由于序列很长且查询次数很大。故需用线段树或树状数组来优化。
线段树的结点信息为左孩子下标、右孩子下标、对应区间数的和。若直接用线段树来处理,只能求出区间连续和,不能求出该区间内不重复数的和,这也是本题的难点所在。若何解决这个无问题呢?我们可以先到离线算法处理。首先读入所有的查询,并对所有查询按区间右端点从小到大排序,然后依次进行处理,这样可以保证后面的操作不会改变前面的查询结果。原序列中的数据一个一个的加入到线段树中,若某个数已经在序列中,则在最近出现的位置删除该数,这样就能保证每个数任意时刻只在线段树的中存储一次;若加入新的数据在线段树中对应的下标等于某个查询区间的右端点,则立即计算该查询区间的和。这样就能保证当前查询区间内没有重复的数,且该区间内的所有数都已加入到线段树中。树状数组的做法与线段树的大致一样,这里就不一一罗列。
算法核心:离线思想+线段树(树状数组)。


1.线段树做法

#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<algorithm>
using namespace std;

const int MAXN=50000+10;
long long ans[200000+10];
long long da[MAXN];
struct node1
{
int index,left,right;
}Q[200000+10];

//************************************************************
struct node
{
int left,right;
long long sum;//sum此处灵活处理
}tree[MAXN*4];
//1.建立以left,right为左右边界,将数组da中元素存储在首地址从1开始的线段树tree的叶节点上
void Build( int id,int left,int right)
{
tree[id].left=left;
tree[id].right=right;
tree[id].sum=0;//此处灵活处理
if(left==right)
{
//tree[id].sum=da[id];//此处可以直接初始化为对应da[id]
return ;
}
else
{
int mid =(left+right)>>1;
Build(id<<1,left,mid);
Build((id<<1)|1,mid+1,right);
//tree[id].sum=tree[(id<<1)].sum+tree[(id<<1)|1].sum;
}
}

//2.在线段树的叶节点pos处加val
void Updata(int id,int pos,long long val)
{
tree[id].sum+=val;
if(tree[id].left==tree[id].right&&tree[id].left==pos)
{
return ;
}
int mid=(tree[id].left+tree[id].right)>>1;
if(pos<=mid)
Updata(id<<1,pos,val);
else
Updata((id<<1)|1,pos,val);
}

//3.查询区间[left,right]上的和
long long Query(int id,int left,int right)
{
if(tree[id].left==left&&tree[id].right==right)
{
return tree[id].sum;
}
int mid=(tree[id].left+tree[id].right)>>1;
if(right<=mid)
return Query(id<<1,left,right);
if(left>=mid+1)
return Query((id<<1)|1,left,right);
return Query(id<<1,left,mid)+Query((id<<1)|1,mid+1,right);
}

bool cmp(node1 a,node1 b)
{
return a.right<b.right;
}

map<long long,int>mat;

int main()
{
int cas,i,n,q,a,id;
cin>>cas;
while(cas--)
{
scanf("%d",&n);
Build(1,1,n);
mat.clear();
for(i=1;i<=n;i++)
{
scanf("%I64d",&da[i]);
}
scanf("%d",&q);
for(i=1;i<=q;i++)
{
scanf("%d%d",&Q[i].left,&Q[i].right);
Q[i].index=i;
}
sort(Q+1,Q+q+1,cmp);
id=1;
for(i=1;i<=n;i++)
{
//  printf("\n******\n");
Updata(1,i,da[i]);
if(mat[da[i]])Updata(1,mat[da[i]],-da[i]);
mat[da[i]]=i;
while(id<=q&&Q[id].right==i)
{
//  printf("\n&&&&&&&\n");
//  printf("Q[id].index=%d    Q[id].left=%d     Q[id].right=%d",Q[id].index,Q[id].left,Q[id].right);
ans[Q[id].index]=Query(1,Q[id].left,Q[id].right);
//  printf("\n&&&&&&&\n");
id++;
}
}
for(i=1;i<=q;i++)
printf("%I64d\n",ans[i]);
}
return 0;
}


2.树状数组做法

#include<iostream>
#include<cstdio>
#include<cstring>
#include<map>
#include<algorithm>
using namespace std;

const int MAXN=50000+10;
long long ans[200000+10];
long long da[MAXN];
struct node1
{
int index,left,right;
}Q[200000+10];
long long C[MAXN];
int Lowbit[MAXN];

//C[i] = a[i-lowbit(i)+1] + …+ a[i],下表从1开始
//Lowbit[i]=i&(i^(i-1));或Lowbit[i]=i&(-i);
//1.查询
long long QuerySum(int p)
//查询原数组中下标1-p的元素的和
{
long long nSum=0;
while(p>0)
{
nSum+=C[p];
p-=Lowbit[p];
}
return nSum;
}

//2.修改+初始化
void Modify(int p,long long val)
//原数组中下表为p的元素+val,导致C[]数组中部分元素值的改变
{
while(p<=MAXN-1)
{
C[p]+=val;
p+=Lowbit[p];
}
}
bool cmp(node1 a,node1 b)
{
return a.right<b.right;
}

map<long long,int>mat;

int main()
{
int cas,i,n,q,a,id;
for(i=1;i<MAXN;i++)
Lowbit[i]=i&(-i);
cin>>cas;
while(cas--)
{
scanf("%d",&n);
memset(C,0,sizeof(C));
mat.clear();
for(i=1;i<=n;i++)
{
scanf("%I64d",&da[i]);
}
scanf("%d",&q);
for(i=1;i<=q;i++)
{
scanf("%d%d",&Q[i].left,&Q[i].right);
Q[i].index=i;
}
sort(Q+1,Q+q+1,cmp);
id=1;
for(i=1;i<=n;i++)
{
Modify(i,da[i]);
if(mat[da[i]])Modify(mat[da[i]],-da[i]);
mat[da[i]]=i;
while(id<=q&&Q[id].right==i)
{
ans[Q[id].index]=QuerySum(Q[id].right)-QuerySum(Q[id].left-1);
id++;
}
}
for(i=1;i<=q;i++)
printf("%I64d\n",ans[i]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: