题目描述

有一个长度为n的数组{a1,a2,…,an}。m次询问,每次询问一个区间内最小没有出现过的自然数。

输入格式

第一行n,m。

第二行为n个数。

从第三行开始,每行一个询问l,r。

输出格式

一行一个数,表示每个询问的答案。

一开始以为像答案这样的做法会超时,没想到真是这样写。。。

 #include<cstdio>
#include<algorithm>
#include<math.h>
#include<string.h>
using namespace std;
const int maxn=2e5+;
int num[maxn];
int answer,block;
int vis[maxn];
struct node
{
int l,r,id;
int Ans;
}ans[maxn];
bool cmp(node x,node y)
{
if(x.l/block!=y.l/block)
return x.l<y.l;
if(x.l/block&) //使用波形排序,可进一步的优化
return x.r<y.r;
return x.r>y.r;
}
bool CMP(node x,node y)
{
return x.id<y.id;
}
void Delete(int pos)
{
vis[num[pos]]--;
if(!vis[num[pos]]&&num[pos]<answer){
answer=num[pos];
}
}
void add(int pos)
{
vis[num[pos]]++;
if(vis[num[pos]]==&&answer==num[pos]){
int tmp=answer+;
while(){
if(!vis[tmp]){
answer=tmp;
return;
}
tmp++;
}
}
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
block=sqrt(n);
for(int i=;i<=n;i++) scanf("%d",&num[i]);
for(int i=;i<=m;i++){
scanf("%d%d",&ans[i].l,&ans[i].r);
ans[i].id=i;
}
sort(ans+,ans++m,cmp);
int left=,right=;
for(int i=;i<=m;i++){
while(ans[i].l>left) Delete(left++);
while(ans[i].l<left) add(--left);
while(ans[i].r>right) add(++right);
while(ans[i].r<right) Delete(right--);
ans[i].Ans=answer;
}
sort(ans+,ans++m,CMP);
for(int i=;i<=m;i++)
printf("%d\n",ans[i].Ans);
return ;
}

还有一个权值线段树的解法:

对于第i棵权值线段树,维护一下数列前ii项中每个权值出现的最靠右的位置。然后向上更新一下最小值。这样如果某个权值最靠右的位置都比查询区间左端点小的话,那它一定没有在这个区间里出现。维护最小值目的就是看最小的是否比左端点大,根据这个在线段树上二分。

权值范围是10^9109的。题解里有说答案最大为nn,直接把>n>n的a[i]a[i]处理为n+1n+1就行。蒟蒻没有想到QAQQAQ,说一下蒟蒻的处理:

用离散化,但是一个权值没有在数列里出现过的话就不会在离散化数组中,也不会出现在线段树中,查询会出锅。注意到一个询问的答案要么是00,要么是数列中某个数的值+1+1。这样离散化时把00和每个出现过的权值+1+1都丢进去就行了QwQQwQ。

当然这道题不强制在线也不修改,可以把询问离线下来一边扫一边加,遇到询问右端点就查询左端点,不需要主席树(可持久化线段树),一棵权值线段树就可以了。

时间复杂度:O(nlogn)

 #include<cstdio>
#include<algorithm>
#include<math.h>
#include<string.h>
#include<vector>
using namespace std;
const int maxn=2e5+;
int ans[maxn];
int t; //T为离散化之后的数组长度
int a[maxn],b[maxn*],cnt; //原数组和离散化的数组,以及下标cnt;
int tree[maxn<<];
int root[maxn];
void update(int root)
{
tree[root]=min(tree[root<<],tree[root<<|]);
}
void add(int num,int l,int r,int root,int base)
{
if(l==r){
tree[root]=base;
return;
}
int mid=l+r>>;
if(num<=mid) add(num,l,mid,root<<,base);
else add(num,mid+,r,root<<|,base);
update(root);
}
int query(int pos)
{
int l=,r=t;
int root=;
while(l<r){
int mid=l+r>>;
//如果左区间的值至少存在一个权值的下标小于pos的,就证明此范围内有符合条件的数;
//就像左边二分;
if(tree[root<<]<pos) r=mid,root=root<<;
//否则向右边二分;
else l=mid+,root=root<<|;
}
return b[l];
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
b[++cnt]=;
for(int i=;i<=n;i++){
scanf("%d",&a[i]);
b[++cnt]=a[i];
b[++cnt]=a[i]+;
}
sort(b+,b++cnt);
t=unique(b+,b++cnt)-b-;
for(int i=;i<=n;i++){
a[i]=lower_bound(b+,b++t,a[i])-b;
add(a[i],,t,,i,root[i],root[i-]);
}
return ;
}

因为权值线段树能解这道题,所以用主席树解就显得没有必要了,但我还是练了练手。

 #include<cstdio>
#include<algorithm>
#include<math.h>
#include<string.h>
using namespace std;
const int maxn=4e5+;
int a[maxn],b[maxn],cnt=,len;
struct node{int l,r,id;}tree[maxn<<];int cot;
//主席树需要开的空间比一般线段树更大,另外后面的cot是记录主席树节点用的。
int Root[maxn];
void build(int &x,int l,int r)
{
//这里的&x会把x得到的值传给这个变量,也就是Root[0];
//建一波空树,其实可以不建的,之前打过不建的代码,
//但总觉得不建的话心里不踏实。啊哈哈哈
x=++cot;
if(l==r) return ;
int mid=l+r>>;
build(tree[x].l,l,mid);
build(tree[x].r,mid+,r);
}
void add(int num,int l,int r,int &x,int y,int pos)
{
//同上,这里的&x会传给root[i];
tree[++cot]=tree[y];
x=cot; //将cot传给x,这个cot储存的是Root[i]这个节点的值,这个节点是根节点。
if(l==r){
tree[cot].id=pos;
//tree[x].id=pos; 这里也可以这样写
return;
}
int mid=l+r>>;
if(num<=mid) add(num,l,mid,tree[x].l,tree[y].l,pos);
else add(num,mid+,r,tree[x].r,tree[y].r,pos);
int left=tree[x].l,right=tree[x].r;
tree[x].id=min(tree[left].id,tree[right].id); //传递最小值;
}
int query(int pos,int base)
{
//这一部分是本题内容。
int l=,r=len;
while(l<r){
int mid=l+r>>;
//left存储的是根节点的左区间,right为右区间。
int left=tree[base].l,right=tree[base].r;
if(tree[left].id<pos) r=mid,base=left;
else l=mid+,base=right;
}
return b[l];
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
b[++cnt]=;
for(int i=;i<=n;i++){
scanf("%d",&a[i]);
b[++cnt]=a[i];
b[++cnt]=a[i]+;
}
sort(b+,b++cnt);
len=unique(b+,b++cnt)-b-;
build(Root[],,len);
for(int i=;i<=n;i++){
a[i]=lower_bound(b+,b++len,a[i])-b;
add(a[i],,len,Root[i],Root[i-],i);
}
for(int i=;i<=m;i++){
int l,r;
scanf("%d%d",&l,&r);
int ans=query(l,Root[r]);
printf("%d\n",ans);
}
return ;
}

莫队或权值线段树 或主席树 p4137的更多相关文章

  1. HDU - 2665 Kth number 主席树/可持久化权值线段树

    题意 给一个数列,一些询问,问$[l,r]$中第$K$大的元素是哪一个 题解: 写法很多,主席树是最常用的一种之一 除此之外有:划分树,莫队分块,平衡树等 主席树的定义其实挺模糊, 一般认为就是可持久 ...

  2. 【BZOJ-2892&1171】强袭作战&大sz的游戏 权值线段树+单调队列+标记永久化+DP

    2892: 强袭作战 Time Limit: 50 Sec  Memory Limit: 512 MBSubmit: 45  Solved: 30[Submit][Status][Discuss] D ...

  3. [BZOJ5139][Usaco2017 Dec]Greedy Gift Takers 权值线段树

    Description Farmer John's nemesis, Farmer Nhoj, has NN cows (1≤N≤10^5), conveniently numbered 1…N. T ...

  4. 【BZOJ4605】崂山白花蛇草水 权值线段树+kd-tree

    [BZOJ4605]崂山白花蛇草水 Description 神犇Aleph在SDOI Round2前立了一个flag:如果进了省队,就现场直播喝崂山白花蛇草水.凭借着神犇Aleph的实力,他轻松地进了 ...

  5. 【bzoj4605】崂山白花蛇草水 权值线段树套KD-tree

    题目描述 神犇Aleph在SDOI Round2前立了一个flag:如果进了省队,就现场直播喝崂山白花蛇草水.凭借着神犇Aleph的实力,他轻松地进了山东省省队,现在便是他履行诺言的时候了.蒟蒻Bob ...

  6. 崂山白花蛇草水 权值线段树套KDtree

    Description 神犇Aleph在SDOI Round2前立了一个flag:如果进了省队,就现场直播喝崂山白花蛇草水.凭借着神犇Aleph的实 力,他轻松地进了山东省省队,现在便是他履行诺言的时 ...

  7. 洛谷P4848 崂山白花蛇草水 权值线段树+KDtree

    题目描述 神犇 \(Aleph\) 在 \(SDOI\ Round2\) 前立了一个 \(flag\):如果进了省队,就现场直播喝崂山白花蛇草水.凭借着神犇 \(Aleph\) 的实力,他轻松地进了山 ...

  8. 【树状数组套权值线段树】bzoj1901 Zju2112 Dynamic Rankings

    谁再管这玩意叫树状数组套主席树我跟谁急 明明就是树状数组的每个结点维护一棵动态开结点的权值线段树而已 好吧,其实只有一个指针,指向该结点的权值线段树的当前结点 每次查询之前,要让指针指向根结点 不同结 ...

  9. BZOJ 3110 ZJOI 2013 K大数查询 树套树(权值线段树套区间线段树)

    题目大意:有一些位置.这些位置上能够放若干个数字. 如今有两种操作. 1.在区间l到r上加入一个数字x 2.求出l到r上的第k大的数字是什么 思路:这样的题一看就是树套树,关键是怎么套,怎么写.(话说 ...

随机推荐

  1. 压缩软件推荐(bandizip)

    提及 Windows 平台的压缩软件,大家往往想起老牌的 WinRAR.开源免费的 7-Zip.国产的快压.好压.360 压缩之类,甚至还有时代的眼泪 WinZip.一直以来,压缩软件因为作为十分基础 ...

  2. mysql 连接权限

    命令解释: . 第一个表示库,第二个表示表; .对全部数据库的全部表授权,so.ok 表示只对so这个库中的ok表授权 root 表示要给哪个用户授权,这个用户可以是存在的用户,也可以是不存在的 '% ...

  3. LaTeX技巧003:实现一个章标题

    代码如下: \documentclass[11pt]{book} \usepackage{graphicx} \usepackage{CJKfntef} \usepackage{color} \mak ...

  4. Codeforce 459A - Pashmak and Garden (已知两点求另外两点构成正方形)

    Pashmak has fallen in love with an attractive girl called Parmida since one year ago... Today, Pashm ...

  5. nginx 启动报错找不到nginx.pid文件

    这个问题的出现应该是系统找不到nginx的配置文件nginx.conf,所以,我们要告诉系统配置文件的位置:' --- 使用nginx -c /usr/local/nginx/conf/nginx.c ...

  6. Selenium3+python自动化009-iframe定位

    iframe 一.frame:HTML页面中的一种框架,主要作用是在当前页面中指定区域显示另一页面元素: 二.操作Frame中的页面元素 定位元素: 1.id定位driver.switch_to.fr ...

  7. Spring Boot2 快速入门教程-到上手

    Spring Boot2 教程合集 入门 纯 Java 代码搭建 SSM 环境 创建一个 Spring Boot 项目的三种方法 理解 Spring Boot 项目中的 parent 基础配置 配置文 ...

  8. selenium爬去数据+存储

    1 爬去数据代码 #coding=utf-8 from selenium import webdriver from selenium.webdriver.common.by import By fr ...

  9. Application Server was not connected before run configuration stop, reason: Unable to ping server at localhost:1099

    方法:把catalina.bat 文件中set JAVA_OPTS= -Xmx1024M -Xms512M -XX:MaxPermSize=256m这行去掉,具体看下面两篇博客 https://blo ...

  10. 素问 - 使用 PE、PB 做估值

    摘自<小韭的学习圈> Q 哪些行业用PE看合适,哪些用PB看合适啊?其中的大致逻辑是什么? A PE = 股价 / 每股收益 使用PE的逻辑是,我们认为一个股票有价值,是因为公司未来能赚钱 ...