题目链接
想到的第一种做法是用树状数组维护每个数出现次数的前缀和。插入某个数就比较简单,树状数组单点修改即可。删除第
k
k
k个数的话就要先二分
p
r
e
f
i
>
=
k
pref_i >= k
prefi>=k的位置,换句话说:找到一个最小的数
v
a
l
val
val使得
∑
i
=
1
v
a
l
c
n
t
i
≥
k
\sum_{i=1}^{val}cnt_i \; ≥ \; k
∑i=1valcnti≥k,然后val的出现次数
−
1
-1
−1,这个复杂度带两个
l
o
g
log
log,904ms.
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const long long mode = 1e9+7;
const int N = 1000010;
struct FenwickTree{
int sum[N];
void init(int n){
for(int i=0;i<=n;i++)sum[i] = 0;
}
void Add(int x,int n,int d){
while( x<=n ){
sum[x] += d;
x += (x&(-x));
}
}
int getSum(int x){
int res = 0;
while( x> 0 ){
res += sum[x];
x -= (x&(-x));
}
return res;
}
};
FenwickTree cnt;
int a[N],q[N];
int bs(int left,int right,int num){
while( left <= right ){
int mid = (left+right) / 2;
if( cnt.getSum( mid ) < num )left = mid+1;
else right = mid-1;
}
return left;
}
int main()
{
int T = 1;
// scanf("%d",&T);
while(T--){
int n,Q;
scanf("%d %d",&n,&Q);
for(int i=1;i<=n;i++)scanf("%d",a+i);
for(int i=1;i<=Q;i++)scanf("%d",q+i);
for(int i=1;i<=n;i++)cnt.Add(a[i],n,1);
for(int i=1;i<=Q;i++){
if( q[i] > 0 )cnt.Add( q[i],n,1 );
else{
int index = bs( 1,n,-q[i] );
cnt.Add( index,n,-1 );
}
}
int index = bs(1,n,1);
printf("%d\n", ( index>n ? 0 : index));
}
return 0;
}
看了题解的最优解,二分最后结果序列中最小的数 m i d mid mid。 c h e c k check check:维护两个计数器 l e s s O r E q u a l lessOrEqual lessOrEqual、 g r e a t e r greater greater,分别记录 < = m i d <=mid <=mid 的数的个数和 > m i d >mid >mid 的数的个数。对于修改:插入一个 q [ i ] q[i] q[i]直接判断是否大于 m i d mid mid,删除第 k k k个就是和 l e s s O r E q u a l lessOrEqual lessOrEqual比较,比它大 g r e a t e r − = 1 greater -= 1 greater−=1,否则 l e s s O r E q u a l − = 1 lessOrEqual-=1 lessOrEqual−=1。最后返回 l e s s O r E q u a l > 0 lessOrEqual > 0 lessOrEqual>0即可。561ms
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const long long mode = 1e9+7;
const int N = 1000010;
int a[N],q[N];
bool check(int mid,int n,int Q){
int le = 0, great = 0;
for(int i=1;i<=n;i++){
if( a[i] <= mid )le++;
else great++;
}
for(int i=1;i<=Q;i++){
if( q[i] > 0 ){
if( q[i] > mid )great++;
else le++;
}
else{
if( -q[i] > le )great--;
else le--;
}
}
// printf("check %d : %d %d\n",mid,le,great);
return le > 0;
}
int main()
{
int T = 1;
// scanf("%d",&T);
while(T--){
int n,Q;
scanf("%d %d",&n,&Q);
for(int i=1;i<=n;i++)scanf("%d",a+i);
for(int i=1;i<=Q;i++)scanf("%d",q+i);
int bottom = 1,top = n;
while( bottom <= top ){
int mid = (bottom + top) / 2;
if( check( mid,n,Q ) )top = mid - 1;
else bottom = mid + 1;
}
printf("%d\n",( top+1 > n ? 0 : top+1 ));
}
return 0;
}