「比赛报告」P7
「比赛报告」P7
来源:校模拟赛。
T1
ez
T2
ez
T3
ez
T4
题目
有一个 \(n\) 个数的序列 \(A\) ,定义 \(f(A)=\left|\{\min\limits_{i=0}^x(\sum\limits_{j=0}^i{A_j})\:|\:x\in[1,n]\}\right|\:\:\:(A_0=0)\) 。
有 \(m\) 次修改操作,每次修改会将 \(A_x\) 修改为 \(y\) 。定义 \(P\) 为 \(A\) 的任意一种排列,在每次修改后输出所有 \(P\) 中不同 \(f(P)\) 的个数。
\(2\leq n,m\leq 2\times 10^5\:,\: 1\leq x\leq n \:,\:-10^9\leq y,A_i\leq 10^9\) 。
思路
显然可以通过将正数都放前面来使得 \(f(P)\) 最大,通过将 \(P\) 从小到大排序使得 \(f(P)\) 最小。
可以证明最大值和最小值之间的数都可以被构造出来。
记 \(f(P)\) 的最大值为 \(r\) ,最小值为 \(l\) 。
\(r=\left|\{A_i\:|A_i>0\wedge i\in[1,n]\}\right|\) 。记 \(x\) 为 \(A\) 从小到大排序后第一个前缀和大于 \(0\) 的位置,\(l=n-x\) 。
现在瓶颈是计算 \(l\) 的复杂度为 \(\mathcal{O(n\log n)}\)
考虑值域线段树上二分。
我们在值域线段树上维护所有 \(A_i>0\) 的 \(A_i\) 。记 \(sum\) 为所有负数的和。
也就是说我们只需要在线段树上找到第一个位置 \(pos\) ,使得所有\(\leq pos\) 的数加起来 \(\geq-sum\) 即可。
复杂度 \(\mathcal{O(n\log V+m\log V)}\) ,\(V\) 为值域。
注意
-
值域比较大,建议离线下来离散化一下。
-
注意有相同数的情况。
#include<bits/stdc++.h>
using namespace std;
const long long MX=200010,INF=1e9;
struct node{
long long lc,rc;
long long sum,siz;
}tree[20000010];
long long root,cnt;
inline void update(long long now){
tree[now].sum=tree[tree[now].lc].sum+tree[tree[now].rc].sum;
tree[now].siz=tree[tree[now].lc].siz+tree[tree[now].rc].siz;
}
void add(long long &now,long long lt,long long rt,long long pos,long long val){
if(lt>pos||rt<pos) return ;
if(!now) now=++cnt;
if(lt==rt){ tree[now].sum+=val;tree[now].siz+=val>0?1:-1;return ;}
long long mid=(lt+rt-1)>>1;
add(tree[now].lc,lt,mid,pos,val),add(tree[now].rc,mid+1,rt,pos,val);
return update(now);
}
void cy(long long now,long long lt,long long rt,long long val,long long &pos){
if(lt==rt){ pos=lt;return ;}
long long mid=(lt+rt-1)>>1;
if(tree[tree[now].lc].sum<val) cy(tree[now].rc,mid+1,rt,val-tree[tree[now].lc].sum,pos);
else cy(tree[now].lc,lt,mid,val,pos);
}
long long query(long long now,long long lt,long long rt,long long l,long long r){
if(rt<l||lt>r) return 0;
if(l<=lt&&rt<=r) return tree[now].siz;
long long mid=(lt+rt-1)>>1;
return query(tree[now].lc,lt,mid,l,r)+query(tree[now].rc,mid+1,rt,l,r);
}
long long query_(long long now,long long lt,long long rt,long long l,long long r){
if(rt<l||lt>r) return 0;
if(l<=lt&&rt<=r) return tree[now].sum;
long long mid=(lt+rt-1)>>1;
return query_(tree[now].lc,lt,mid,l,r)+query_(tree[now].rc,mid+1,rt,l,r);
}
long long input[MX]={0};
signed main(){
long long n,m;scanf("%lld%lld",&n,&m);
long long sum=0,sx=0;bool flag=1;
for(long long i=1;i<=n;i++){ scanf("%lld",&input[i]),sum+=input[i]<=0,sx+=input[i]*(input[i]<=0),flag&=input[i]>0;if(input[i]>0) add(root,0,INF,input[i],input[i]);}
while(m--){
long long x,y;scanf("%lld%lld",&x,&y);flag&=y>0;
if(y<=0) sum++,sx+=y;
else add(root,0,INF,y,y);
if(input[x]<=0) sum--,sx-=input[x];
else add(root,0,INF,input[x],-input[x]);
if(flag){puts("1");continue;}
input[x]=y;
long long pox=0;cy(root,0,INF,-sx,pox);
long long minn=query(root,0,INF,0,pox-1),maxn=n-sum;
minn+=min((-sx-query_(root,0,INF,0,pox-1))/pox,query(root,0,INF,pox,pox));
minn=n-sum-minn;
printf("%lld\n",maxn-minn+1);
}
return 0;
}