P4587-[FJOI2016]神秘数【主席树】
生活随笔
收集整理的這篇文章主要介紹了
P4587-[FJOI2016]神秘数【主席树】
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
正題
題目鏈接:https://www.luogu.com.cn/problem/P4587
題目大意
nnn個數,每次選擇一個區間,然后詢問這個區間的子集和所不能表示的最小的正整數。
解題思路
假設我們從小到大加入數字,我們發現如果這個數不是111顯然這個區間內至少有一個111。
我們假設現在能表示的最小的數是mxmxmx,加入一個數xxx時,如果x>mx+1x> mx+1x>mx+1那么mx+1mx+1mx+1就不能表現出來,否則我們就可以表示出1~mx+x1\sim mx+x1~mx+x的數,即讓mx=mx+xmx=mx+xmx=mx+x。
考慮如何優化,如果我們之前能表現出1~mx1\sim mx1~mx,加上某些數之后能表現出1~mx′1\sim mx'1~mx′了,那么我們顯然可以尋找任何一個在mx+1~mx′+1mx+1\sim mx'+1mx+1~mx′+1里的數字來擴充,那么我們之間將這段區間的和取來不就好了嗎。這個可以用主席樹維護。
時間復雜度如何,我們發現每次找到的區間和如果還能往下顯然是大于mxmxmx的,然后會讓mx′+mxmx'+mxmx′+mx也就是每次至少可以讓mx′mx'mx′加上自己的一半,這個時間復雜度是接近倍增也就是logloglog的。
所以時間復雜度O(nlog?2n)O(n\log^2 n)O(nlog2n)
codecodecode
#include<cstdio> #include<cstring> #include<algorithm> #define ll long long using namespace std; const ll N=1e6+10,M=5e6+10,inf=1e9; ll n,m,cnt,rt[N]; ll ls[M],rs[M],sum[M]; ll Change(ll x,ll L,ll R,ll val){ll y=++cnt;rs[y]=rs[x];ls[y]=ls[x];ll mid=(L+R)>>1;sum[y]=sum[x]+val;if(L==R)return y;if(val<=mid)ls[y]=Change(ls[x],L,mid,val);else if(val>mid)rs[y]=Change(rs[x],mid+1,R,val);return y; } ll Ask(ll x,ll y,ll L,ll R,ll l,ll r){if(!(sum[x]-sum[y]))return 0;if(l==L&&r==R)return sum[x]-sum[y];ll mid=(L+R)>>1;if(r<=mid)return Ask(ls[x],ls[y],L,mid,l,r);if(l>mid)return Ask(rs[x],rs[y],mid+1,R,l,r);return Ask(ls[x],ls[y],L,mid,l,mid)+Ask(rs[x],rs[y],mid+1,R,mid+1,r); } int main() {scanf("%lld",&n);for(ll i=1;i<=n;i++){ll x;scanf("%lld",&x);rt[i]=Change(rt[i-1],1,inf,x);}scanf("%lld",&m);for(ll i=1;i<=m;i++){ll l,r,lim=0,at=0;scanf("%lld%lld",&l,&r);while(1){ll w=Ask(rt[r],rt[l-1],1,inf,lim+1,at+1);if(!w)break;lim=at+1;at+=w;}printf("%lld\n",at+1);}return 0; }總結
以上是生活随笔為你收集整理的P4587-[FJOI2016]神秘数【主席树】的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 脚气怎么根治,治脚气最有效的方法
- 下一篇: P3899-[湖南集训]谈笑风生【主席树