【题目描述】
HDU - 5919 Sequence II

【题目分析】
题目给定一个数组,每次查询一个区间,找出区间内不同数字的个数x,然后输出按出现顺序第x/2向上取整个数字的位置。

  • 按照要求,我们首先需要能够找出给定区间不同的数字个数。
    首先,我们分析一个简单一些的问题:对于右端点固定的区间,如何计算不同左区间内不同数字的个数。
    我们不妨用一个数组记录cntcntcnt哪些位置出现了一个不同的数字,用sumsumsum数组进行维护cnt[1..l]cnt[1..l]cnt[1..l]的和(可以用线段树或者树状数组),那么对于区间[l,r][l,r][l,r]内不同数字的个数就是sum[r]−sum[l−1]sum[r]-sum[l-1]sum[r]−sum[l−1]。
    在从前往后进行添加的过程中,如果该数字在前面已经出现,就将前面的标记消除,在后面的位置进行标记,也就是说尽可能将标记后放。例如对于数组1,2,2,3,5,11,2,2,3,5,11,2,2,3,5,1维护以后的cntcntcnt数组就是0,0,1,1,1,10,0,1,1,1,10,0,1,1,1,1,这样做的原因是我们假设的是右端点固定,对于重复的元素,在后面如果出现过前面就没有必要标记。
    如果询问是离线的,我们大可以先将询问保存下来,然后从前往后加入数据的过程中不断将对应的询问答案保存(对应是指右端点相同),最后输出就可以了。
    可是这个问题是强制在线的,所以我们必须使用主席树进行可持久化。可是这种可持久化和以前的主席树运用不同,因为在添加的过程中会将前面的标记消除,所以不同根节点的主席树不在拥有可以互相加减的能力(加减的结果不再有意义)。然而在我们这个问题里面我们是不需要进行加减的。
  • 不同于求区间第K大的时候我们的主席树维护的是值区间,即值在区间内的个数,这里根节点的1..n1..n1..n指的是数据范围aiaiai的最大值。在这里我们的根节点的1..n1..n1..n的nnn指的是数据的个数,就是题目中的nnn,标记的是该位置上出现了一个之前没有出现过的数字。 因此对于每次询问,我们访问的版本里保存的就是实际的数组,直接计数就可以。而区间第K大就需要减去之前的版本才是该区间内的数的个数。
  • 题目要求的是数据第一次出现的位置,可是按照上面的想法进行建树的话我们保存的是当前区间[1,i][1,i][1,i]数据最后一次出现的位置。很自然,我们应该进行逆序建树,这样的话我们保存的就是[i,n][i,n][i,n]区间内数据第一次出现的位置,对于需要查询的区间[l,r][l,r][l,r],我们访问第lll个版本的主席树,就满足题目要求啦。
    求出数字的个数后我们再除以2向上取整就是题目要求的k,然后在找出对应的位置,这里类似区间第K大
    【参考文献】
    大佬博客1
    大佬博客2
    【AC代码】
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
#include<set>
#include<climits>
#include<string>
#include<cmath>
#include<cstdlib>using namespace std;const int MAXN=200005;
int a[MAXN];
int n,m;
struct node
{int ls,rs,cnt;
}tree[MAXN*40];
int root[MAXN*40];
int b[MAXN];
int tot;void Insert(int &now,int pre,int x,int l,int r,int add)
{int tmp=now; now=++tot;tree[now]=tmp?tree[tmp]:tree[pre];tree[now].cnt+=add;if(l==r) return;int mid=(l+r)>>1;if(x<=mid) Insert(tree[now].ls,tree[pre].ls,x,l,mid,add);else Insert(tree[now].rs,tree[pre].rs,x,mid+1,r,add);
}int GetSum(int k,int l,int r,int L,int R)
{if(l>=L && r<=R) return tree[k].cnt;int ret=0; int mid=(l+r)>>1;if(L<=mid) ret+=GetSum(tree[k].ls,l,mid,L,R);if(R>mid) ret+=GetSum(tree[k].rs,mid+1,r,L,R);return ret;
}int query(int k,int l,int r,int x)
{if(l==r) return l;int mid=(l+r)>>1;int tmp=tree[tree[k].ls].cnt;if(x<=tmp) query(tree[k].ls,l,mid,x);else query(tree[k].rs,mid+1,r,x-tmp);
}int main()
{int T,ans,l,r,tmp,sum;scanf("%d",&T);for(int Case=1;Case<=T;Case++){scanf("%d%d",&n,&m);memset(tree,0,sizeof(tree));memset(a,0,sizeof(a));memset(b,0,sizeof(b));memset(root,0,sizeof(root));tot=0;for(int i=1;i<=n;i++){scanf("%d",&a[i]);}for(int i=n;i>0;i--){if(b[a[i]]) Insert(root[i],root[i+1],b[a[i]],1,n,-1);Insert(root[i],root[i+1],i,1,n,1);b[a[i]]=i;}ans=0;printf("Case #%d:",Case);//测试 //printf("\n");for(int i=0;i<m;i++){scanf("%d%d",&l,&r);l=(l+ans)%n+1; r=(r+ans)%n+1;if(l>r) tmp=l,l=r,r=tmp;//printf("test: l=%d r=%d\n",l,r);sum=GetSum(root[l],1,n,l,r);//printf("test: sum=%d\n",sum);sum=(sum+1)/2;//printf("test: sum=%d\n",sum);ans=query(root[l],1,n,sum);printf(" %d",ans);}printf("\n");}return 0;
}

HDU - 5919 Sequence II——主席树+区间种类++逆序建树相关推荐

  1. HDU 5919 Sequence II 主席树

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=5919 Sequence II Time Limit: 9000/4500 MS (Java/Othe ...

  2. HDU - 5919 Sequence II(主席树+思维)

    题目链接:点击查看 题目大意:给出一个长度为 n 的数列 a ,再给出 m 次询问,每次询问给出一个区间 [ l , r ] ,问区间 [ l , r ] 内首次出现的数字的位置的中位数 题目分析:题 ...

  3. HDU - 4348To the moon——主席树+区间修改

    HDU - 4348To the moon [题目描述] [题目分析] 题目中说明每次更新后时间都会加1,而且还会需要查询以前的区间,还会需要返回以前的时间,所以是很裸的主席树.区间查询的话我们同样需 ...

  4. HDU - 5919 Sequence II

    题意: 给定长度为n的序列和q次询问.每次询问给出一个区间(L,R),求出区间内每个数第一次出现位置的中位数,强制在线. 题解: 用主席树从右向左的插入点.对于当前点i,如果a[i]出现过,则把原位置 ...

  5. Hdu-5919 Sequence II(主席树在线求区间不同数)

    Description Mr. Frog has an integer sequence of length n, which can be denoted as a1,a2,⋯,an There a ...

  6. HDU - 3397 Sequence operation(线段树+区间合并)

    题目链接:点击查看 题目大意:给定一个初始的数列,然后依次进行m次操作: 0 a b:将闭区间[a,b]内的点都变为0 1 a b:将闭区间[a,b]内的点都变为1 2 a b:将闭区间[a,b]内的 ...

  7. hdu 3397 Sequence operation(线段树,lazy,区间合并)

    hdu 3397 Sequence operation 线段树lazy和区间合并结合的一个题,相当于几个题集中到一起嘛,分开想就好了 0,1,2操作都要lazy,2的异或操作找到每一只含1或只含0的区 ...

  8. HDU 4348 To the moon(主席树区间修改)

    题意 给你一个区间,支持如下操作: 在一段区间内加上一个值,并生成一个历史版本 查询某个版本下一段区间内的和 回到一个历史版本上并舍弃之后的版本 做法 这就是主席树区间修改裸题啦QwQ 上一篇博客我讲 ...

  9. HDU - 4348 To the moon(主席树区间更新-标记永久化)

    题目链接:点击查看 题目大意:给出一个初始时长度为 n 的序列,有 m 次操作,每种操作分为下列四种类型: C l r d:新建一个继承了前一个版本的数组,并将区间 [ l , r ] 内的数字都加上 ...

最新文章

  1. 中国雪糕红黑榜,谁才是Top 1
  2. SAP MM PIR里的Lower Limit Upper Limit
  3. hdu 4751(dfs染色)
  4. JUC并发编程十 并发架构--Unsafe
  5. centos磁盘空间满查询和移动命令小记
  6. python图像识别车票_是程序员就用Python查12306的票
  7. TensorFlow和ML前5名的课程
  8. python一图带你精通time类型转换
  9. React中的this指向问题
  10. 波波老师: 解决微服务的数据一致性分发问题?
  11. 函数yield报错ValueError: too many values to unpack (expected 2)
  12. oracle11g调整表空间和临时表空间大小
  13. 有以下程序C语言a b cdef,[工学]全国计算机二级笔试基础部分和C语言程序设计.doc...
  14. 暑假集训 div1 B Derangement 交换数字 思维死角
  15. 向数据库插入数据时出现乱码 --设置连接数据库的编码
  16. MAC 开发环境搭建及工具
  17. matlab激光扩束总结,zemax笔记14——激光扩束系统的设计
  18. 【WEB搜索技术】课程学习大纲与学习感悟
  19. hyperf 热重启
  20. kali虚拟机安装提示安装系统步骤失败

热门文章

  1. PHP中unset,array_splice删除数组中元素的区别
  2. 万网与阿里巴巴业务关系图解
  3. linux mint 18.3 内核,Linux Mint 18.3 “Sylvia” Cinnamon正式发布上线
  4. java scrollpane 设置透明_java swing 之 JScrollPane(滚动面板)的使用
  5. oppo 手机侧滑快捷菜单_OPPO刚秀出卷轴屏手机,就被打了一记响亮的“耳光”
  6. python利用opencv去除图片logo_利用python和opencv批量去掉图片黑边
  7. python pyplot中axis_Python Pyplot xaxis未显示在图形上
  8. 哈师大计算机等级考试,哈尔滨师范大学教务处
  9. C语言程序设计孙家啸第一版,广东年月自考各专业课程使用教材.doc
  10. react 判断图片是否加载完成_React中型项目的优化实践