HDU-3333-Turing Tree-离散化思想

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

这个题目很好!!!真的很好!!!思维能力的锻炼!!!

                题目意思:给你个数组,然后多次查询,每次查询一个区间,叫你求这个区间内所有不重复的数字之后;数据量很大,想用暴力是不可能的;

                       思路:线段树+离线处理;也就是说我先把你每次要查询的区间保存下来,并且将他们按右区间由小到大排好序,同时我们也保存题目提供的数组,

                                  而且不暂时不更新到线段树里面,这里我们就得换个角度去想了,就这么简单的更新到树里面,那算得结果肯定是错误的啊,因为前面加过了;

 

 

                                  我们形象的来说一下,我每次更新一个数字,我判断前面有没有出现过,如果有出现过,我就将前面的那个数字清零,并且将这个数字存在最后一次

                                  出现的位置;但是这样有的人就会说,这样岂不是会改变后面的结果吗?这里就有一个技巧了,那就是我代码里面提到的那样,把区间先排序,这样我每次

                                  更新到区间最靠左的那个区间,就直接先把结果算出来,后面在更新,然后再清零,这样对我数据就没有影响;

 

呼呼,说的有些啰嗦,好像还不是很清楚;可以结合我的代码加注释,理解一下;也可以结合一下这个链接里说的思路,还清晰;

链接:http://www.cnblogs.com/deadblue/archive/2012/09/13/2683199.html

求助一下大家,就是我那个为什么用lower_bound()函数会WA,而用我代码里的二分查找就可以AC,结果是一样的啊,希望可以不吝赐教;感谢。。。ORZ...

#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<vector>
#include<algorithm>
#define LL long long
using namespace std;
const int N=30015;
const int M=100015;
struct node
{
    int l,r;
    LL v;
}node[N<<2];
struct NODE     //  存储查询的区间,及下标;
{
    int l,r,id;
}temp[M];
void PushUp(int numb)       //  向上更新父节点数据;
{
    node[numb].v=node[numb<<1].v+node[numb<<1|1].v;
}
void build(int l,int r,int numb)        //  建立线段数
{
    node[numb].l=l;
    node[numb].r=r;
    node[numb].v=0;
    if(l==r) return;
    int mid=(l+r)>>1;
    build(l,mid,numb<<1);
    build(mid+1,r,numb<<1|1);
}
void Insert(int numb,int t,int v)       //  数据的更新,插入;
{
    int l=node[numb].l,r=node[numb].r;
    if(l==r){
        node[numb].v+=v;
        return;
    }
    int mid=(l+r)>>1;
    if(t>mid) Insert(numb<<1|1,t,v);
    else if(t<=mid) Insert(numb<<1,t,v);
    PushUp(numb);
}
LL query(int l,int r,int numb)          //  区间查询;
{
    if(l==node[numb].l&&r==node[numb].r) return node[numb].v;
    int mid=(node[numb].l+node[numb].r)>>1;
    if(l>mid) return query(l,r,numb<<1|1);
    else if(r<=mid) return query(l,r,numb<<1);
    else return query(l,mid,numb<<1)+query(mid+1,r,numb<<1|1);
}
bool cmp(NODE x,NODE y)         //  自定义结构体排序;
{
    return x.r<y.r;
}
int a[N],b[N],bb[N],visit[N];
LL ans[M];
int Is(int v,int k){            //  二分查找,其中v是要找的数,k表示数组的个数;
    int mid,l=1,r=k;
    while(l<=r){
        mid=(l+r)/2;
        if(bb[mid]>v) r=mid-1;
        else if(bb[mid]<v) l=mid+1;
        else return mid;
    }
    return 0;
}
int main()
{
    int t,m,n;
    scanf("%d",&t);
    while(t--){
        scanf("%d",&n);
        build(1,30015,1);       //  建树;
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);  //  先将数据用数组保存;
            b[i]=a[i];          //  为了不破坏数据的顺序,再用一个数组,
        }
        sort(b,b+n+1);
        bb[1]=b[1];
        int k=1;
        for(int i=2;i<=n;i++){  //  去重;
            if(b[i]!=b[i-1]){
                bb[++k]=b[i];
            }
        }
        memset(ans,0,sizeof(ans));      //  初始化;
        memset(visit,0,sizeof(visit));
        scanf("%d",&m);
        for(int i=1;i<=m;i++){          //  先将要查询的数据用结构体保存;
            scanf("%d%d",&temp[i].l,&temp[i].r);
            temp[i].id=i;               //  保存下标,这样排序后仍能够和结果相对应保存;
        }
        sort(temp,temp+m+1,cmp);        //  按右区间由小到大排序;
        int j=1;
        for(int i=1;i<=n;i++){
            //int id=lower_bound(bb,bb+n+1,a[i])-bb;    //    第一个大于等于值a[i]的位置,用这个函数提交就WA,想了一下午也没有想明白为什么,
            int id=Is(a[i],k);          //  返回该元素下标;    用这个写的二分查找就可以AC,ORZ...如果谁懂希望可以教我。。。跪求
            if(visit[id]){              //  如果之前出现过,那么就将前面的清零;因为我们是计算区间不重复数字的和,所以对结果没有影响;
                Insert(1,visit[id],-a[i]);
            }
            Insert(1,i,a[i]);
            visit[id]=i;                //  标记出现过,并且记录出现的位置;
            for(;j<=m;j++){
                if(i==temp[j].r){       //  这就是我们为什么要讲区间排序的原因,因为只有这样我们才可以保证在清零之后不影响结果;所以将
                    LL sum=query(temp[j].l,temp[j].r,1);            //  这个查询放在插入里面,只要插入的点有我要查找的右区间,
                    ans[temp[j].id]+=sum;                           //  我就立马算出结果,避免后面出现的数字把之前的数据清零;
                }else break;
            }
        }
        for(int i=1;i<=m;i++) printf("%lld\n",ans[i]);
    }
    return 0;
}


 

你可能感兴趣的:(线段树,难题,HDU3333,离散思想)