您的位置:首页 > 其它

BZOJ4408 - [FJOI2016]神秘数

2018-04-24 21:26 288 查看

Portal

Description

一个可重复数字集合\(S\)的神秘数定义为最小的不能被\(S\)的子集的和表示的正整数。现给出一个\(n(n\leq10^5)\)个数的数列\(\{a_n\}(\Sigma a_i\leq10^9)\)和\(m(m\leq10^5)\)次询问,每次给出两个数\(L,R\),求集合\(\{a_{L..R}\}\)的神秘数。

Solution

可持久化线段树。
首先考虑如何求出集合的神秘数。定义\(t\)和\(ans\),表示用不超过\(t\)的元素能够凑成\([1,ans-1]\)中的所有数。初始时有\(t=0,ans=1\)。接下来考虑加上一个数\(x\)后对\(ans\)有何影响。若\(x>ans\)则依然无法凑出\(ans\),不变;否则能够凑出\([1,ans+x-1]\)中的所有数,\(ans=ans+x\)。那么求出\([t+1,ans]\)中所有元素的和,加到\(ans\)上就得到了新的\(ans\),新的\(t\)等于原\(ans\)。简化一下过程:初始\(ans=1\),每次令\(ans\)等于\([1,ans]\)中所有元素的和\(+1\),直到\(ans\)不再增大为止。由于\(ans\)每次如果增大则至少增大\(t+1\),所以最多进行\(log\)次。
那么建立\(n\)棵权值线段树分别记录前若干个数中的权值分布情况,询问区间\([L,R]\)时用线段树\(R\)减线段树\(L-1\)即可。

时间复杂度\(O(mlog^2n)\)。

Code

//[FJOI2016]神秘数
#include <cstdio>
typedef long long lint;
inline char gc()
{
static char now[1<<16],*s,*t;
if(s==t) {t=(s=now)+fread(now,1,1<<16,stdin); if(s==t) return EOF;}
return *s++;
}
inline int read()
{
int x=0; char ch=gc();
while(ch<'0'||'9'<ch) ch=gc();
while('0'<=ch&&ch<='9') x=x*10+ch-'0',ch=gc();
return x;
}
const int N=1e5+10;
int n,m;
int cnt,rt
;
struct node{int chL,chR; lint sum;} nd[N*32];
void update(int p) {nd

.sum=nd[nd[p].chL].sum+nd[nd[p].chR].sum;} void ins(int &p,int L0,int R0,int x) { nd[++cnt]=nd[p]; p=cnt; if(L0==R0) {nd[p].sum+=x; return;} int mid=L0+R0>>1; if(x<=mid) ins(nd[p].chL,L0,mid,x); else ins(nd[p].chR,mid+1,R0,x); update(p); } lint optL,optR; lint qres; void query(int p1,int p2,int L0,int R0) { if(p1==p2) return; if(optL<=L0&&R0<=optR) {qres+=nd[p2].sum-nd[p1].sum; return;} int mid=L0+R0>>1; if(optL<=mid) query(nd[p1].chL,nd[p2].chL,L0,mid); if(mid<optR) query(nd[p1].chR,nd[p2].chR,mid+1,R0); } int main() { n=read(); int maxA=1e9; for(int i=1;i<=n;i++) ins(rt[i]=rt[i-1],1,maxA,read()); m=read(); for(int i=1;i<=m;i++) { int L=read(),R=read(); lint ans=1; while(true) { optL=1,optR=ans,qres=0,query(rt[L-1],rt[R],1,maxA); if(qres<ans) break; else ans=qres+1; } printf("%lld\n",ans); } return 0; }

P.S.

[p]我又开始懒得写题解了...
本来我想权值\(10^9<2^{30}\)所以线段树只开了\(30N\)个节点,但事实上应该开\(31N\) 0(xp )~

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: