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;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-10-07 07:36:35

HDU-3333-Turing Tree-离散化思想的相关文章

HDU 3333 Turing Tree (离散化+离线处理+树状数组)

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 t

HDU 3333 Turing Tree 树状数组 离线查询

题意: 给你一个数列,然后有n个查询,问你给定区间中不同数字的和是多少. 思路还是比较难想的,起码对于蒟蒻我来说. 将区间按照先右端点,后左端点从小到大排序之后,对于每个查询,我只要维护每个数字出现的最后一次就可以了(这个结论稍微想一下就可以证明是正确的). 然后就是简单的点更新,区间求和问题了- #include <cstdio> #include <cstring> #include <iostream> #include <map> #include

hdu 3333 Turing Tree(线段树)

题目链接:hdu 3333 Turing Tree 题目大意:给定一个长度为N的序列,有M次查询,每次查询l,r之间元素的总和,相同元素只算一次. 解题思路:涨姿势了,线段树的一种题型,离线操作,将查询按照右区间排序,每次考虑一个询问,将mv ~ r的点全部标记为存在,并且对于每个位置i,如果A[i]在前面已经出现过了,那么将前面的那个位置减掉A[i],当前位置添加A[i],这样做维护了每个数尽量做,那么碰到查询,用sum[r] - sum[l-1]即可. #include <cstdio>

HDU 3333 Turing Tree(树状数组离线处理)

HDU 3333 Turing Tree 题目链接 题意:给定一个数组,每次询问一个区间,求出这个区间不同数字的和 思路:树状数组离线处理,把询问按右端点判序,然后用一个map记录下每个数字最右出现的位置,因为一个数字在最右边出现,左边那些数字等于没用了,利用树状数组进行单点修改区间查询即可 代码: #include <cstdio> #include <cstring> #include <algorithm> #include <map> using n

hdu 3333 Turing Tree (树状数组+离线处理+离散化)

Turing Tree Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 3981    Accepted Submission(s): 1349 Problem Description After inventing Turing Tree, 3xian always felt boring when solving problems a

HDU 3333 Turing Tree(离线树状数组)

Turing Tree Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 5014    Accepted Submission(s): 1777 Problem Description After inventing Turing Tree, 3xian always felt boring when solving problems

HDU 3333 Turing Tree

Turing Tree http://acm.hdu.edu.cn/showproblem.php?pid=3333 分析: 这道题目叫 图灵树. 离线+树状数组. 维护到每个右端点的答案,直接查询左端点,树状数组维护.一个数字不能出现两次,会发现如果出现了多个数,最右边的数可以代替左边的所有数,所以只加入最右边一个数即可. 代码: 1 #include<cstdio> 2 #include<algorithm> 3 #include<cstring> 4 #inclu

HDU 3333 Turing Tree (离线询问+线段树)

题目地址:HDU 3333 将询问离线保存下来,然后将数组的点离散化,记录每个值上一次出现的位置.然后枚举数组的数,若当前枚举的数前面出现过,那么就删掉前面出现过的那个位置上的数,更新当前这个位置上的数,然后那些所有询问的右端点为当前位置的就可以通过查询来得到结果了. 更新与查询用线段树来优化. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #

HDU 3333 Turing Tree(离线线段树)

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 t

hdu 3333 Turing Tree(树状数组离线操作)

Turing Tree Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 3904    Accepted Submission(s): 1325 Problem Description After inventing Turing Tree, 3xian always felt boring when solving problems