大鸽子 llmmkk 正在补8.3号咕掉的题

时隔两个月,再看到这道题,我又是一脸懵,这种思维的培养太重要了


链接:

P4587


题意:

给出 \(n\) 个点的序列,\(m\) 次询问区间神秘数

神秘数定义为最小的不能被序列的子集的和表示的正整数。

如序列 \(\{1,1,4,1,13\}\) 的神秘数是 \(8\)。


分析:

这题重点在神秘数的求法,先考虑暴力求法,由于定义是序列子集,那么首先考虑将该区间排序,可能会得到一些有用的性质。

假设当前能够表示的区间是 \([1,sum]\),对于当前的数 \(x\) 。

  1. \(x>sum+1\),那么答案就是 \(sum+1\)。
  2. \(x\leq sum+1\),那么能够表示的区间将会变成 \([1,sum+x]\),继续考虑下一个数。

仔细想一下会发现这个暴力很对。

然后考虑优化,暴力是一个一个判断并处理的,我们考虑一次性处理多个 \(x\)。于是正解就是:

假设当前能够表示的区间是 \([1,sum]\),记区间内所有小于等于 \(sum\) 的 \(x\) 之和为 \(res\)。

  1. \(res+1>sum\),那么将 \(sum\) 更新为 \(res+1\)
  2. \(res+1\leq sum\) ,那么答案就是 \(sum+1\)

仔细想想这个东西,发现它成功做到了上面的优化,同时它的正确性也很对。因为这个做法实在太难以言传了,所以可以再参考一下这篇

时间复杂度分析:假如当前的神秘数为 \(s1\),下一个神秘数是 \(s2\),再下一个是 \(s3\),那么从 \(s2\) 到 \(s3\) 相比从 \(s1\) 到 \(s2\) 多出来的 \(x\) 一定是大于 \(s1\) 的,所以 \(ans\) 是成倍增长的,于是这个做法的复杂度就是 \(O(\log\sum a)\)


算法:

于是我们需要一个东西能够维护区间内某个值域的数值之和。可以在每个位置维护一个从 \(1\) 到当前位置的权值线段树,拉到主席树上,然后两个区间查询相减就可以做到。

主席树 \(O(n\log n)\),上面的算法 \(O(\log\sum a)\),所以总复杂度 \(O(n\log n\log\sum a)\)。


代码:
#include <bits/stdc++.h>
using namespace std;
#define in read()
inline int read(){
int p=0,f=1;
char c=getchar();
while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
while(isdigit(c)){p=p*10+c-'0';c=getchar();}
return p*f;
}
const int N=1e5+5;
int n,m;
int a[N],q[N],qn,w[N];
int bin(int key){
int l=1,r=qn,mid;
while(l<r){
mid=(l+r+1)>>1;
if(q[mid]<=key)l=mid;
else r=mid-1;
}
return l;
}
int rt[N];
int tot,sum[N<<5],lc[N<<5],rc[N<<5];
int newnode(){
++tot;
sum[tot]=lc[tot]=rc[tot]=0;
return tot;
}
void pushup(int p){
sum[p]=sum[lc[p]]+sum[rc[p]];
}
int fi_built(int l,int r){
int p=newnode();
if(l==r){return p;}
int mid=(l+r)>>1;
lc[p]=fi_built(l,mid);
rc[p]=fi_built(mid+1,r);
return p;
}
int built(int l,int r,int pre,int x,int d){
int now=newnode(),mid=(l+r)>>1;
sum[now]=sum[pre],lc[now]=lc[pre],rc[now]=rc[pre];
if(l==r){sum[now]+=d*q[l];return now;}
if(x<=mid)lc[now]=built(l,mid,lc[now],x,d);
else rc[now]=built(mid+1,r,rc[now],x,d);
pushup(now);
return now;
}
int query(int l,int r,int p,int ql,int qr){
if(ql>qr)return 0;
if(l>=ql&&r<=qr)return sum[p];
int mid=(l+r)>>1,res=0;
if(ql<=mid)res+=query(l,mid,lc[p],ql,qr);
if(qr>mid)res+=query(mid+1,r,rc[p],ql,qr);
return res;
}
signed main(){
n=in;
for(int i=1;i<=n;i++)
a[i]=in,q[i]=a[i];
sort(q+1,q+1+n);
qn=unique(q+1,q+1+n)-(q+1);
rt[0]=fi_built(1,n);
for(int i=1;i<=n;i++)
rt[i]=built(1,n,rt[i-1],bin(a[i]),1);
m=in;
for(int i=1;i<=m;i++){
int l=in,r=in;
int ans=1,tans=bin(ans);
int que=query(1,n,rt[r],1,tans)-query(1,n,rt[l-1],1,tans);
while(que+1>ans){
ans=que+1;
tans=bin(ans);
que=query(1,n,rt[r],1,tans)-query(1,n,rt[l-1],1,tans);
}
cout<<ans<<'\n';
}
return 0;
}

洛谷 P4587 [FJOI2016]神秘数的更多相关文章

  1. 洛谷P4587 [FJOI2016]神秘数(主席树)

    题面 洛谷 题解 考虑暴力,对于询问中的一段区间\([l,r]\),我们先将其中的数升序排序,假设当前可以表示出\([1,k]\)目前处理\(a_i\),假如\(a_i>k+1\),则答案就是\ ...

  2. Luogu P4587 [FJOI2016]神秘数

    一道好冷门的好题啊,算是对于一个小结论和数据结构的一点考验吧 首先看完题目我们发现要从这个神秘数的性质入手,我们观察or手玩可得: 如果有\(x\)个\(1\),那么\([1,x]\)都是可以表示出来 ...

  3. P4587 [FJOI2016]神秘数(主席树)

    题意:给出1e5个数 查询l,r区间内第一个不能被表示的数 比如1,2,4可以用子集的和表示出[1,7] 所以第一个不能被表示的是8 题解:先考虑暴力的做法 把这个区间内的数字按从小到大排序后 从前往 ...

  4. 220722 T4 求和 /P4587 [FJOI2016]神秘数 (主席树)

    好久没打主席树了,都忘了怎么用了...... 假设我们选了一些数能构成[0,x]范围内的所有值,下一个要加的数是k(k<=x+1),那么可以取到[0,x+k]内的所有取值,所以有一种做法: 对于 ...

  5. LUOGU P4587 [FJOI2016]神秘数(主席树)

    传送门 解题思路 如果区间内没有\(1\),那么答案就为\(1\),从这一点继续归纳.如果区间内有\(x\)个\(1\),设区间内\([2,x+1]\)的和为\(sum\),如果\(sum=0\),那 ...

  6. 【BZOJ4408】[FJOI2016]神秘数(主席树)

    [BZOJ4408][FJOI2016]神秘数(主席树) 题面 BZOJ 洛谷 题解 考虑只有一次询问. 我们把所有数排个序,假设当前可以表示出的最大数是\(x\). 起始\(x=0\). 依次考虑接 ...

  7. 【LG4587】[FJOI2016]神秘数

    [LG4587][FJOI2016]神秘数 题面 洛谷 题解 首先我们想一想暴力怎么做 对于一段区间\([l,r]\) 我们先将它之间的数升序排序 从左往右扫, 设当前我们可以表示出的数为\([1,x ...

  8. (bzoj4408)[FJOI2016]神秘数(可持久化线段树)

    (bzoj4408)[FJOI2016]神秘数(可持久化线段树) bzoj luogu 对于一个区间的数,排序之后从左到右每一个数扫 如果扫到某个数a时已经证明了前面的数能表示[1,x],那么分情况: ...

  9. BZOJ4299 & CC FRBSUM:ForbiddenSum & BZOJ4408 & 洛谷4587 & LOJ2174:[FJOI2016]神秘数——题解

    https://www.lydsy.com/JudgeOnline/problem.php?id=4299 https://www.lydsy.com/JudgeOnline/problem.php? ...

随机推荐

  1. Haproxy搭建web集群

    目录: 一.常见的web集群调度器 二.Haproxy应用分析 三.Haproxy调度算法原理 四.Haproxy特性 五.Haproxy搭建 Web 群集 一.常见的web集群调度器 目前常见的we ...

  2. go实现堆排序、快速排序、桶排序算法

    一. 堆排序 堆排序是利用堆这种数据结构而设计的一种排序算法.以大堆为例利用堆顶记录的是最大关键字这一特性,每一轮取堆顶元素放入有序区,就类似选择排序每一轮选择一个最大值放入有序区,可以把堆排序看成是 ...

  3. 手把手教你实现栈以及C#中Stack源码分析

    定义 栈又名堆栈,是一种操作受限的线性表,仅能在表尾进行插入和删除操作. 它的特点是先进后出,就好比我们往桶里面放盘子,放的时候都是从下往上一个一个放(入栈),取的时候只能从上往下一个一个取(出栈), ...

  4. CSS linear-gradient() 函数

    用于背景颜色渐变或画线条等场景 linear-gradient() 函数用于创建一个表示两种或多种颜色线性渐变的图片. 创建一个线性渐变,需要指定两种颜色,还可以实现不同方向(指定为一个角度)的渐变效 ...

  5. rabbitmqctl 命令行管理工具

    1. 用户管理 用户管理包括增加用户,删除用户,查看用户列表,修改用户密码. (1) 新增一个用户 rabbitmqctl add_user Username Password (2) 删除一个用户 ...

  6. Shell系列(5)- 输出输入重定向及wc命令

    输出重定向: 在Linux当中,0代表输入:1代表正确输出:2代表错误输出 类型 符号 作用 正确输出重定向 命令 > 文件 以覆盖得方式,把命令得正确输出,输出到指定文件或设备当中 命令 &g ...

  7. 初探DispatcherServlet#doDispatch

    初探DispatcherServlet#doDispatch 写在前面 SpringBoot其实就是SpringMVC的简化版本,对于request的处理流程大致是一样的, 都要经过Dispatche ...

  8. 判断javaScript变量是Ojbect类型还是Array类型

      JavaScript是弱类型的语言,所以对变量的类型并没有强制控制类型.所以声明的变量可能会成为其他类型的变量, 所以在使用中经常会去判断变量的实际类型. 对于一般的变量我们会使用typeof来判 ...

  9. P3643-[APIO2016]划艇【dp】

    正题 题目链接:https://www.luogu.com.cn/problem/P3643 题目大意 求有多少个\(n\)个数的序列\(x\)满足,\(x_i\in \{0\}\cup[a_i,b_ ...

  10. YbtOJ#532-往事之树【广义SAM,线段树合并】

    正题 题目链接:https://www.ybtoj.com.cn/problem/532 题目大意 给出\(n\)个点的一个\(Trie\)树,定义\(S_x\)表示节点\(x\)代表的字符串 求$$ ...