hdu-3874-Necklace-(树状数组)

来源:互联网 发布:php curl异步请求 编辑:程序博客网 时间:2024/06/05 14:37

Necklace

Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 5022    Accepted Submission(s): 1716


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
261 2 3 4 3 531 23 52 661 1 1 2 3 531 12 43 5
 

Sample Output
3714136
 

Source
2011 Multi-University Training Contest 4 - Host by SDU
 

Recommend
lcy   |   We have carefully selected several similar problems for you:  3878 3875 3870 3877 3876 

题意: 给出一个序列,和m次询问,每次询问给出一个区间,问这个区间内不同的元素的和是多少

做法无非是,对从数组元素求前缀和,对于每个区间减去相同元素值,但这里用树状数组来做,利用树状数组插入元素改变总和的快捷性。

这里要定义一个指针it,然后对于区间循环,其实it 指针在内部已经对每个a[i]进行处理,每当到达一个区间终点时,记录结果,在区间内部时如果当前it指针指向元素已经出现过那么删除上一次出现此元素,元素上一次出现时的下标用vis数组记录,然后将元素加入如树状数组。

有重复的就删掉前一个的话,树状数组里元素就都是单一的,如果两者在同一区间正好符合题意,如果相同两者不是在同一区间那么由于是对区间终点升序排列的,删掉也没有影响。

代码:

#include<iostream>#include<string>#include<cstdio>#include<algorithm>#include<cmath>#include<iomanip>#include<queue>#include<cstring>#include<map>using namespace std;typedef long long ll;int n,m;ll tree[50005],ans[200005]; //tree是树状数组,ans是记录结果的数组int a[50005],vis[1000005]; //a数组存放元素,vis数组记录元素上一次出现时的下标struct node{    int s,e,id;    bool operator <(const node& b) const //对区间按终点升序排列,终点相同按起点升序排    {        return e<b.e||(e==b.e&&s<b.s);    }}p[200005];int lowbit(int i){    return i&(-i);}void add(int i,int v){    while(i<=n)    {        tree[i]+=v;        i+=lowbit(i);    }}ll sum(int i)  //一定注意i>=1,切记i不能为0{    ll res=0;    while(i>0)    {        res+=tree[i];        i-=lowbit(i);    }    return res;}int main(){    int i,T;    scanf("%d",&T);    while(T--)    {        scanf("%d",&n);        memset(tree,0,sizeof(tree));        memset(vis,0,sizeof(vis));        for(i=1;i<=n;i++)        {            scanf("%d",&a[i]);        }        scanf("%d",&m);        for(i=1;i<=m;i++)        {            scanf("%d%d",&p[i].s,&p[i].e);            p[i].id=i;        }        sort(p+1,p+m+1);        int it=1;                //it是当前元素下标        for(i=1;i<=m;i++)        {            while(it<=p[i].e) //相当于正在处理第i区间            {                if(vis[a[it]]!=0) //元素出现过,删除上次出现的那个相同元素                {                    add(vis[a[it]],-a[it]);                }                vis[a[it]]=it;  //记录元素a[it]出现时下标                add(it,a[it]);  //加入树状数组                it++;            }            ans[p[i].id]=sum(p[i].e)-sum(p[i].s-1); //求的是第p[i].id个区间的结果        }        for(i=1;i<=m;i++)        {            printf("%I64d\n",ans[i]);        }    }    return 0;}


原创粉丝点击