- 一个长度为(n)的序列(a),令(P)为所有正数之和,(N)为所有负数之和,定义(w_i=egin{cases}frac{a_i}P&a_i>0,\frac{a_i}{-N}&a_i<0end{cases})。
- (q)次操作,每次修改某一个(a_i)。
- 在所有操作之前及每次操作之后,求出使(s_i=sum_{x=1}^iw_x)最大的最小(i)。
- (n,qle5 imes10^4)
叉积的转化
我们先给所有(w_i)乘上(P imes(-N))消去分母。
考虑我们记(vec{p_i}=(sum_{x=1}^{i}a_x[a_x>0],sum_{x=1}^i-a_x[a_x<0])),并令(vec{S}=vec{p_n}),发现:
[s_i=sum_{x=1}^ia_x[a_x>0] imes(-N)-sum_{x=1}^i-a_x[a_x<0] imes P=vec{p_i} imes vec{S}
]
也就是说,(s_i)被我们转化成了一个关于(i)的向量与一个固定向量的叉积。
由于叉积的最大值一定在凸包上,我们只要维护一个凸壳然后每次在其上二分即可。
分块维护凸壳
由于叉积是满足分配律的,我们可以用分块来维护,定义新的(vec{p_i})仅表示这个块内的前缀和。
这样一来,单点修改就只要重构一个块的凸壳就好了。
询问的时候我们枚举每个块,在这个块的凸壳上二分,给二分结果加上前面块之和与(S)的叉积值,然后更新答案。
复杂度为(O(q(S+frac nSlog S))),取(S=sqrt{nlogn})得到最优复杂度(O(nqsqrt{nlogn}))。
代码:(O(nqsqrt{nlogn}))
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 50000
#define BS 888
#define BT 60
#define LL long long
using namespace std;
int n,a[N+5];struct P
{
LL x,y;I P(Con LL& a=0,Con LL& b=0):x(a),y(b){}
I P operator + (Con P& o) Con {return P(x+o.x,y+o.y);}
I P operator - (Con P& o) Con {return P(x-o.x,y-o.y);}
I LL operator ^ (Con P& o) Con {return 1LL*x*o.y-1LL*y*o.x;}
}p[N+5];
namespace FastIO
{
#define FS 100000
#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
#define D isdigit(oc=tc())
int ff,OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
Tp I void read(Ty& x) {x=0,ff=1;W(!D) ff=oc^'-'?1:-1;W(x=(x<<3)+(x<<1)+(oc&15),D);x*=ff;}
Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('
');}
}using namespace FastIO;
struct Block
{
int cnt,id[BS+5];P T;I void Build(CI l,CI r)//建凸壳
{
cnt=0,T=P();for(RI i=l;i<=r;id[++cnt]=i++)
{p[i]=T=T+(a[i]>0?P(a[i],0):P(0,-a[i]));W(cnt>1&&((T-p[id[cnt]])^(p[id[cnt]]-p[id[cnt-1]]))>0) --cnt;}
}
I pair<LL,int> Q(Con P& S)//凸壳上二分
{
RI l=1,r=cnt,mid;W(l^r) mid=l+r-1>>1,((p[id[mid+1]]-p[id[mid]])^S)<=0?r=mid:l=mid+1;
return make_pair(p[id[r]]^S,id[r]);
}
}B[BT+5];
int sz,bl[N+5];P S;I void Build()//预处理
{
RI i;for(sz=max((int)sqrt(n*log2(n)),1),i=1;i<=n;++i) bl[i]=(i-1)/sz+1;
for(i=1;i<=bl[n];++i) B[i].Build((i-1)*sz+1,min(i*sz,n)),S=S+B[i].T;
}
I void U(CI x,CI v)//单点修改
{
S=S-B[bl[x]].T,a[x]=v,B[bl[x]].Build((bl[x]-1)*sz+1,min(bl[x]*sz,n)),S=S+B[bl[x]].T;//重构这个块
}
I int Q()//询问
{
if(!S.x||!S.y) return n;pair<LL,int> ans=make_pair((LL)-1e18,0),t;P T;//如果全正或全负答案必然为n
for(RI i=1;i<=bl[n];++i) ((t=B[i].Q(S)).first+=T^S)>ans.first&&(ans=t,0),T=T+B[i].T;return ans.second;//枚举每个块,加上前面块之和与S的叉积值
}
int main()
{
RI Qt,i;for(read(n,Qt),i=1;i<=n;++i) read(a[i]);Build(),writeln(Q());
RI x,y;W(Qt--) read(x,y),U(x,y),writeln(Q());return clear(),0;
}