題解 P4587 【[FJOI2016]神祕數】
阿新 • • 發佈:2020-11-21
P4587 [FJOI2016]神祕數
題意簡述:
一個可重複數字集合S的神祕數定義為最小的不能被S的子集的和表示的正整數。例如S={1,1,1,4,13}
現給定n個正整數a[1]..a[n],m個詢問,每次詢問給定一個區間l,r,求由a[l],a[l+1],…,a[r]所構成的可重複數字集合的神祕數。
解析
先探究神祕數的有關性質。
假設我們是把數一個一個插入序列中的(這也是主席樹的解題過程)
設當前能組成的數的值域為 \([1,x]\) ,要插入的數為 \(a_i\)
有如下性質:
-
當 \(a_i>x+1\) 時,\(x+1\) 不能被組成,故此時神祕數為 \(x+1\)
-
當 \(a_i<x+1\)
若每插入一個 \(a_i\) 就新增一個版本,那麼對於每個版本:
設它的值域為 \([1,x]\),則答案 \(ans=x+1\) 。
此時檢查小於等於 \(ans\) 的所有數的和 \(sum\)
若 \(ans\leq sum\), 則一定有未選的且小於等於 \(ans\) 的數。
則令 \(ans = res+1\),重複檢查。
若 \(ans > sum\),則答案就是 \(ans\)。
這裡維護的並不是權值線段樹,離散化什麼的也不用了。
code :
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=1e5+10; struct node { int lson,rson; ll _sum; } tree[N<<5]; int root[N],tot=0; int n,m; int ans=0,res=0; #define lnode tree[node].lson #define rnode tree[node].rson #define DEFMID ll mid=start+end>>1; #define lnode1 tree[node1].lson #define rnode1 tree[node1].rson int insert(int node,ll start,ll end,int x) { int node1=++tot; tree[node1]=tree[node]; tree[node1]._sum+=x; if(start==end) { return node1; } DEFMID if(x<=mid) lnode1=insert(lnode,start,mid,x); else rnode1=insert(rnode,mid+1,end,x); tree[node1]._sum=tree[lnode1]._sum+tree[rnode1]._sum; return node1; } ll query(int node1,int node,ll start,ll end,ll l,ll r) { if(l<=start&&end<=r) return tree[node1]._sum-tree[node]._sum; DEFMID ll ans=0; if(l<=mid) ans+=query(lnode1,lnode,start,mid,l,r); if(r>mid) ans+=query(rnode1,rnode,mid+1,end,l,r); return ans; } int main() { scanf("%d",&n); for(int i=1; i<=n; i++) { int x; scanf("%d",&x); root[i]=insert(root[i-1],1,1e9,x); } scanf("%d",&m); for(int i=1; i<=m; i++) { int l,r; scanf("%d%d",&l,&r); ans=1; while(1) { res=query(root[r],root[l-1],1,1e9,1,ans); if(res>=ans) ans=res+1; else break; } printf("%d\n",ans); } return 0; }