莫队或权值线段树 或主席树 p4137
题目描述
有一个长度为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的更多相关文章
- HDU - 2665 Kth number 主席树/可持久化权值线段树
题意 给一个数列,一些询问,问$[l,r]$中第$K$大的元素是哪一个 题解: 写法很多,主席树是最常用的一种之一 除此之外有:划分树,莫队分块,平衡树等 主席树的定义其实挺模糊, 一般认为就是可持久 ...
- 【BZOJ-2892&1171】强袭作战&大sz的游戏 权值线段树+单调队列+标记永久化+DP
2892: 强袭作战 Time Limit: 50 Sec Memory Limit: 512 MBSubmit: 45 Solved: 30[Submit][Status][Discuss] D ...
- [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 ...
- 【BZOJ4605】崂山白花蛇草水 权值线段树+kd-tree
[BZOJ4605]崂山白花蛇草水 Description 神犇Aleph在SDOI Round2前立了一个flag:如果进了省队,就现场直播喝崂山白花蛇草水.凭借着神犇Aleph的实力,他轻松地进了 ...
- 【bzoj4605】崂山白花蛇草水 权值线段树套KD-tree
题目描述 神犇Aleph在SDOI Round2前立了一个flag:如果进了省队,就现场直播喝崂山白花蛇草水.凭借着神犇Aleph的实力,他轻松地进了山东省省队,现在便是他履行诺言的时候了.蒟蒻Bob ...
- 崂山白花蛇草水 权值线段树套KDtree
Description 神犇Aleph在SDOI Round2前立了一个flag:如果进了省队,就现场直播喝崂山白花蛇草水.凭借着神犇Aleph的实 力,他轻松地进了山东省省队,现在便是他履行诺言的时 ...
- 洛谷P4848 崂山白花蛇草水 权值线段树+KDtree
题目描述 神犇 \(Aleph\) 在 \(SDOI\ Round2\) 前立了一个 \(flag\):如果进了省队,就现场直播喝崂山白花蛇草水.凭借着神犇 \(Aleph\) 的实力,他轻松地进了山 ...
- 【树状数组套权值线段树】bzoj1901 Zju2112 Dynamic Rankings
谁再管这玩意叫树状数组套主席树我跟谁急 明明就是树状数组的每个结点维护一棵动态开结点的权值线段树而已 好吧,其实只有一个指针,指向该结点的权值线段树的当前结点 每次查询之前,要让指针指向根结点 不同结 ...
- BZOJ 3110 ZJOI 2013 K大数查询 树套树(权值线段树套区间线段树)
题目大意:有一些位置.这些位置上能够放若干个数字. 如今有两种操作. 1.在区间l到r上加入一个数字x 2.求出l到r上的第k大的数字是什么 思路:这样的题一看就是树套树,关键是怎么套,怎么写.(话说 ...
随机推荐
- Selenium3+python自动化013-自动化数据驱动及模型介绍
一.查看当前运行的浏览等相关信息 driver=webdriver.Chrome() print(driver.capabilities["version"]) #浏览器版本 pr ...
- webpack如何编译ES6打包
前言:随着ES的普及我们越来越多的开始使用ES6的语法了,当然也随着mvvm框架的流行少不了js模块化,那js模块化又有那些呢 在很早的时候大家都用的命名空间,现在也有人用(库名.类别名.方法名) 后 ...
- [SUCTF 2019]EasySQL(堆叠注入配合sql_mode)
考点:1.堆叠注入 2.set sql_mode=PIPES_AS_CONCAT;将||视为字符串的连接操作符而非或运算符 意外:注入* 复现: 1;set sql_mode=PIPES_AS_CON ...
- redis 安装以及配置
centos 安装 yum install redis-server 配置 配置文件redis.conf 配置访问密码 # requirepass foobared requirepass 指定密码1 ...
- 巨杉内核笔记 | 会话(Session)
SequoiaDB 巨杉数据库是一款金融级分布式关系型数据库,坚持从零开始打造分布式开源数据库引擎.“内核笔记系列”旨在分享交流 SequoiaDB 巨杉数据库引擎的设计思路和代码解析,帮助社区用户深 ...
- OpenGL 编程指南 (5.1)
1.OpenGL支持同时使用多个纹理单元,使用GL_TEXTUREi进行标识,使用前需要先激活对应的纹理单元,默认GL_TEXTURE0是激活绑定的. void glActiveTexture(GLe ...
- 金蝶云星空使用WebAPI来新增单据
有很多客户需求在后台自动生成某张单据,金蝶云星空提供了WebApi,包含了保存,提交,审核,删除单据的接口,下面以生产订单的保存,提交,审核为例,说明一下应用WebApi后台自动生成生产订单的功能,下 ...
- EF CodeFirst配置领域类
当我们不想使用EF的默认约定时,可以手动配置领域类,但还是推荐少配置,Simple is best! 两种配置方式: 1.Data Annotation Attributes[数据注解特性] 数据注 ...
- 注解 - @Deprecated
意思是说此方法已过时,过时的原因就是有新的API的类替代了次方法. 这个被划去的方法仍然是可以正常使用的,就是一个提示而已. Java内在的File类中有如下方法 @Deprecated public ...
- IntelliJ IDEA常规配置教程
IntelliJ IDEA,是java编程语言开发的集成环境.IntelliJ在业界被公认为最好的java开发工具,尤其在智能代码助手.代码自动提示.重构.J2EE支持.各类版本工具(git.sv ...