BZOJ4408: [Fjoi 2016]神秘数

$n \leq 100000$个正整数,$m \leq 100000$个询问,每次问区间$[L,R]$不在这几个数组成的可重集合的子集数字和的集合的最小正整数,说人话就是这几个数瞎选加起来不能凑成的最小正整数。

以为是数学题。。没想到是一主席树。。

可以考察集合里加入一个数之后对集合不能凑成的数的影响。假设原先集合最小不能凑成的数为$x$,加入了一个$y$,如果$y>x$那没意义,如果$y<=x$,相当于把一个背包移动$y$位后和原来的或起来,那么1到$x+y-1$都可以取到,答案至少应该是$x+y$(可能原来就能取到这个数,所以答案会更大但不会更小);再判断是不是答案时,需要考察$\leq x+y$的所有数字。

由于$x->x+y$是由“$x$是原来的答案,且$ \leq x$的数的和超过了$x$”,那可不可以得出这样的结论:$x$从1开始验证,原答案是$x$时,$\leq x$的数的和$sum$超过了$x$,那么$1$到$sum$的数字都是能得到的!

证明:当$x=1$时显然。当$x>1$时,假设上一个验证的答案是$y$。如果比$x$小的数加起来补到$x$那$x$就得不到;如果加起来超过$x$,由于$\leq y$的数加起来到了$x-1$,因此一定有一些数字$>y$。把能得到的数放在一个背包里,一个$>y$而$\leq x$的数$a$会把这个背包移动$a$位然后或起来,由于这个数$\leq x$,而原来背包$1$到$x-1$都是已经可达的,因此这样的一次移动+取或不会在$1$到$x+z-1$之间造成空隙,因此每个数在背包里移动+取或后,能取到$1$到$x-1+这些>y而\leq x$的数且不留空隙。

那复杂度呢?最坏的情况下,$y->x$之后每次增添一个数$y+1$,这样可构造数列$1,2,3,5,8,13……$这数列有点熟??所以复杂度是约等于$logMax$的,每次需要对一个区间找小于等于某个数的数的和,上主席树。复杂度$m*log_2n*log_2Max$。

 1 //#include<iostream>
 2 #include<cstring>
 3 #include<cstdlib>
 4 #include<cstdio>
 5 //#include<time.h>
 6 //#include<complex>
 7 #include<algorithm>
 8 #include<stdlib.h>
 9 using namespace std;
10 
11 int n,m;
12 #define maxn 100011
13 int root[maxn];
14 int a[maxn],lisa[maxn],li=0;
15 struct SMT
16 {
17     struct Node
18     {
19         int ls,rs;
20         int sum;
21     }a[maxn*20];
22     int size,n;
23     void clear(int m) {size=0; a[0].sum=0; n=m;}
24     void insert(int &x,int y,int L,int R,int pos)
25     {
26         x=++size; a[x].sum=a[y].sum+lisa[pos];
27         if (L==R) {a[x].ls=a[x].rs=0; return;}
28         int mid=(L+R)>>1;
29         if (pos<=mid) insert(a[x].ls,a[y].ls,L,mid,pos),a[x].rs=a[y].rs;
30         else insert(a[x].rs,a[y].rs,mid+1,R,pos),a[x].ls=a[y].ls;
31     }
32     void insert(int &x,int y,int pos) {insert(x,y,1,n,pos);}
33     int query(int x,int y,int v)
34     {
35         v=upper_bound(lisa+1,lisa+1+li,v)-lisa;
36         if (v==li+1) return a[y].sum-a[x].sum;
37         int ans=0,L=1,R=n;
38         while (L<R)
39         {
40             int mid=(L+R)>>1;
41             if (v<=mid) x=a[x].ls,y=a[y].ls,R=mid;
42             else ans+=a[a[y].ls].sum-a[a[x].ls].sum,x=a[x].rs,y=a[y].rs,L=mid+1;
43         }
44         return ans;
45     }
46 }t;
47 
48 int main()
49 {
50     scanf("%d",&n);
51     for (int i=1;i<=n;i++) scanf("%d",&a[i]),lisa[++li]=a[i];
52     sort(lisa+1,lisa+1+n); li=unique(lisa+1,lisa+1+li)-lisa-1; t.clear(li);
53     for (int i=1;i<=n;i++) a[i]=lower_bound(lisa+1,lisa+1+li,a[i])-lisa,t.insert(root[i],root[i-1],a[i]);
54     scanf("%d",&m);
55     while (m--)
56     {
57         int x,y; scanf("%d%d",&x,&y);
58         int ans=1,tmp;
59         while ((tmp=t.query(root[x-1],root[y],ans))>=ans) ans=tmp+1;
60         printf("%d\n",ans);
61     }
62     return 0;
63 }
View Code

 

posted @ 2018-03-14 11:44  Blue233333  阅读(184)  评论(0编辑  收藏  举报