bzoj 4408: [Fjoi 2016]神秘数
额,一开始突然想到了如果能表示出连续的二进制位,就可以构造出连续的数了。。然后想了一下,不可做2333
于是又走上了扒题解的不归路。。
貌似题解就是推广一下??
如果能表示出[l,r]那么新加入一个数a,那么可以得到一个新的区间是[l+a,r+a],然后和 [l,r]and[l+a,r+a](and表示取并集)就是现在能表示的区间。
现在我们希望 [l,r]and[l+a,r+a]==[l,r+a] ,这样的话考虑a的加入顺序,显然是应该从小到大的。
而且,在[l,r]and[l+a,r+a]==[l,r+a]的条件下,a是可解的:l+a-1<=r+1 (且l==1)
现在,a是从小到大加入的,而r肯定是小于等于a的所有值得和,带入的话,就可以判断出当前的a是否成立,从而就知道是不是有答案了。
然后判断区间小于一个数,用主席树搞一下就就好了。
这样的话,a是迭代变大,大概log次就好。。
1 #include <bits/stdc++.h> 2 #define LL long long 3 using namespace std; 4 inline int ra() 5 { 6 int x=0; char ch=getchar(); 7 while (ch<'0' || ch>'9') ch=getchar(); 8 while (ch>='0' && ch<='9') {x=x*10+ch-'0'; ch=getchar();} 9 return x; 10 } 11 12 const int maxn=100005; 13 const int N=maxn*40; 14 15 int ls[N],rs[N],sum[N],tot; 16 int root[maxn],len; 17 void insert(int l, int r, int x, int &y, int pos, int val) 18 { 19 y=++tot; sum[y]=sum[x]+val; 20 if (l>=r) return; 21 int mid=l+r>>1; 22 ls[y]=ls[x]; rs[y]=rs[x]; 23 if (pos<=mid) insert(l,mid,ls[x],ls[y],pos,val); 24 else insert(mid+1,r,rs[x],rs[y],pos,val); 25 } 26 int query(int x, int y, int pos) 27 { 28 int l=1,r=len-1,ans=0; x=root[x-1]; y=root[y]; 29 while (l<r) 30 { 31 int mid=l+r>>1; 32 if (pos<=mid) x=ls[x],y=ls[y],r=mid; 33 else ans+=sum[ls[y]]-sum[ls[x]],l=mid+1,x=rs[x],y=rs[y]; 34 } 35 return ans+sum[y]-sum[x]; 36 } 37 38 int n,a[maxn],num[maxn],ans; 39 int main(int argc, char const *argv[]) 40 { 41 n=ra(); 42 for (int i=1; i<=n; i++) num[i]=a[i]=ra(); 43 sort(num+1,num+n+1); 44 len=unique(num+1,num+n+1)-num; 45 for (int i=1; i<=n; i++) a[i]=lower_bound(num+1,num+len,a[i])-num; 46 for (int i=1; i<=n; i++) insert(1,len-1,root[i-1],root[i],a[i],num[a[i]]); 47 int T=ra(); 48 while (T--) 49 { 50 int l=ra(),r=ra(),t; 51 for (ans=1;;ans=t+1) 52 { 53 int orz=lower_bound(num+1,num+len,ans)-num; 54 orz=(orz==len || num[orz]>ans)?orz-1:orz; 55 t=query(l,r,orz); 56 if (t<ans) break; 57 } 58 printf("%d\n",ans); 59 } 60 return 0; 61 }