BZOJ4408: [Fjoi 2016]神祕數
阿新 • • 發佈:2019-01-04
題意
給你\(n\)個數\(A_i...A_n\),每次詢問一個區間\([l,r]\),問這個區間中的數不能組成的最小正整數是多少;
題解
遇到這種題詢問的題一般先考慮如果只詢問一次,一次詢問整個數列怎麼做;
考慮把數從小到大排序,然後求出字首和\(S_1...S_n\)接下來有一個比較顯然但是非常關鍵的結論:不能表示的最小的正整數,一定是某個位置的字首和+1(包含\(0\)和\(n\));設\(F[i]=[1,0]\)表示小於等於\(i\)的正整數能否全部被表示出來,那麼我們要找的就是這麼一個位置:最小的\(j\)使得\(F[A_j]=0\),那麼不能表示出的最小正整數便是\(S_{j-1}+1\);考慮如果某個位置\(t\),\(F[A_t]=1\),那麼\(S_t\)一定大於等於\(A_t\),那麼這前\(t\)個數一定能夠把\(1,2,3..S_t\)全部表示出來(具體證明只需要把數拆成二進位制位看看就很清楚了),那麼轉移就有了兩種情況,如果\(A_{t+1}>S_t+1\)則\(F[A_{t+1}]=0\),否則\(F[A_{t+1}]=1\);知道了轉移我們便有了一種很快找到這個位置的辦法了,考慮初始能表示的數為\(s\),\(s\)一開始等於\(0\),然後接下來小於等於\(s+1\)的數全部能被表示出來,找到最多能擴充套件到的位置\(p\),那麼現在全部能被表示出來的數便成了\(S_p\)(字首和),這樣每次去擴充套件直到擴充套件不動,最終的答案便是能擴充套件到的位置的字首和+1;因為每次會出現一個新的最小的能被表示的數至少比已知能擴充套件的大\(1\),那麼最壞情況也只會擴充套件\(log\)次(考慮序列\(1,2,4,8,16..\)每次只擴充套件一個位置,但因為題目條件所有數總和小於等於\(1e9\),所以最多擴充套件30次也就能全部擴充套件完了);
然後是區間查詢,其實我們需要的也就是查區間中有多少個數小於等於某數,上主席樹就做完了;
#include<bits/stdc++.h> #define Fst first #define Snd second #define RG register #define mp make_pair #define mem(a,b) memset(a,b,sizeof(a)) using namespace std; typedef long long LL; typedef long double LD; typedef unsigned int UI; typedef unsigned long long ULL; template<typename T> inline void read(T& x) { char c = getchar(); bool f = false; for (x = 0; !isdigit(c); c = getchar()) { if (c == '-') { f = true; } } for (; isdigit(c); c = getchar()) { x = x * 10 + c - '0'; } if (f) { x = -x; } } template<typename T, typename... U> inline void read(T& x, U& ... y) { read(x), read(y...); } const int N=1e5+10; int n,Q,SUM,CNT,size; int A[N],V[N],root[N]; map<int,int> S; struct Node { int lo,ro,cnt,sum; }Tr[N*20]; void Modify(int l,int r,int &o,int pos,int v) { Tr[++size]=Tr[o]; o=size; ++Tr[o].cnt; Tr[o].sum+=v; if(l==r) return; int mid=l+r>>1; if(pos<=mid) Modify(l,mid,Tr[o].lo,pos,v); else Modify(mid+1,r,Tr[o].ro,pos,v); } void Find(int l,int r,int x,int y,int sum) { if(l==r) { if(V[l]<=sum) SUM+=Tr[x].sum-Tr[y].sum,CNT+=Tr[x].cnt-Tr[y].cnt; return; } int mid=l+r>>1; if(V[mid]<=sum) SUM+=Tr[Tr[x].lo].sum-Tr[Tr[y].lo].sum,CNT+=Tr[Tr[x].lo].cnt-Tr[Tr[y].lo].cnt,Find(mid+1,r,Tr[x].ro,Tr[y].ro,sum); else Find(l,mid,Tr[x].lo,Tr[y].lo,sum); } int main() { // ios::sync_with_stdio(false); #ifdef rua freopen("GG.in","r",stdin); #endif read(n); for(int i=1;i<=n;++i) read(A[i]),V[i]=A[i]; sort(V+1,V+n+1); int cnt=unique(V+1,V+n+1)-V-1; for(int i=1;i<=cnt;++i) S[V[i]]=i; for(int i=1;i<=n;++i) Modify(1,cnt,root[i]=root[i-1],S[A[i]],A[i]); read(Q); while(Q--) { int l,r; read(l,r); int last=0,sum=0; while(1) { CNT=SUM=0; Find(1,cnt,root[r],root[l-1],sum+1); if(CNT==last) break; last=CNT; sum=SUM; } printf("%d\n",sum+1); } return 0; }