题目:神秘数
解析:
主席树。
看半天题一搜题解主席树,???
先考虑
O
(
N
M
l
o
g
N
)
O(NMlogN)
O(NMlogN)的做法,将区间内的数从小到大排序。假设
[
1
,
r
]
[1,r]
[1,r]能用
S
S
S集合中的数表示。然后如果当前加入一个数字
a
a
a,则可以分为两类讨论:
1.若
a
<
=
r
+
1
a<=r+1
a<=r+1,则当前可以将区间变长为:
[
1
,
r
+
a
]
[1,r+a]
[1,r+a]。
2.若
a
>
r
+
1
a>r+1
a>r+1,则
r
+
1
r+1
r+1凑不出来,答案即为
r
+
1
r+1
r+1。
考虑如何优化,我们换种思路,不进行排序。同样假设当前已经组合好了
[
1
,
r
]
[1,r]
[1,r],设
a
n
s
=
r
+
1
ans=r+1
ans=r+1,初始时
a
n
s
=
1
ans=1
ans=1。 我们在区间内统计一下权值小等于
a
n
s
ans
ans的点的权值和,设为
x
x
x。
如果
a
n
s
<
=
x
ans<=x
ans<=x,说明一定存在一个数满足小等于
r
+
1
r+1
r+1,这个时候
[
1
,
x
]
[1,x]
[1,x]都可以组合出来,将
a
n
s
ans
ans更新到
x
+
1
x+1
x+1。
如果
a
n
s
>
x
ans>x
ans>x,说明其余所有的数均大于
r
+
1
r+1
r+1,这样当前的
a
n
s
ans
ans就是答案。
具体实现用主席树。
至于复杂度,容易发现这样做每次
r
r
r都是成倍增加的,所以最终复杂度为
O
(
N
l
o
g
M
l
o
g
∑
i
=
1
n
a
[
i
]
)
O(NlogMlog\sum_{i=1}^{n}a[i])
O(NlogMlogi=1∑na[i])
代码:
#include <bits/stdc++.h>
using namespace std;
const int Max=100005;
int n,m,sum,tot;
int num[Max],tree[Max*40],lc[Max*40],rc[Max*40],rt[Max];
inline int get_int()
{
int x=0;char c;
for(c=getchar();!isdigit(c);c=getchar());
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x;
}
inline void print(int x)
{
if(x>9) print(x/10);
putchar('0'+x%10);
}
inline void add(int fa,int &now,int l,int r,int x)
{
now=++tot,tree[now]=tree[fa]+x,lc[now]=lc[fa],rc[now]=rc[fa];
if(l==r) return;
int mid=(l+r)>>1;
if(x<=mid) add(lc[fa],lc[now],l,mid,x);
else add(rc[fa],rc[now],mid+1,r,x);
}
inline int Q(int rt1,int rt2,int l,int r,int x)
{
if(l==r) return tree[rt1]-tree[rt2];
int mid=(l+r)>>1;
if(x<=mid) return Q(lc[rt1],lc[rt2],l,mid,x);
else return tree[lc[rt1]]-tree[lc[rt2]]+Q(rc[rt1],rc[rt2],mid+1,r,x);
}
int main()
{
n=get_int();
for(int i=1;i<=n;i++) num[i]=get_int(),sum+=num[i];
for(int i=1;i<=n;i++) add(rt[i-1],rt[i],1,sum,num[i]);
m=get_int();
for(int i=1;i<=m;i++)
{
int l=get_int(),r=get_int(),ans=1,x;
while(1)
{
x=Q(rt[r],rt[l-1],1,sum,ans);
if(x>=ans) ans=x+1;
else break;
}
print(ans),putchar('\n');
}
return 0;
}