【洛谷7028】[NWRRC2017] Joker(分塊+凸殼)
阿新 • • 發佈:2021-06-19
- 一個長度為\(n\)的序列\(a\),令\(P\)為所有正數之和,\(N\)為所有負數之和,定義\(w_i=\begin{cases}\frac{a_i}P&a_i>0,\\\frac{a_i}{-N}&a_i<0\end{cases}\)。
- \(q\)次操作,每次修改某一個\(a_i\)。
- 在所有操作之前及每次操作之後,求出使\(s_i=\sum_{x=1}^iw_x\)最大的最小\(i\)。
- \(n,q\le5\times10^4\)
叉積的轉化
我們先給所有\(w_i\)乘上\(P\times(-N)\)消去分母。
考慮我們記\(\vec{p_i}=(\sum_{x=1}^{i}a_x[a_x>0],\sum_{x=1}^i-a_x[a_x<0])\)
也就是說,\(s_i\)被我們轉化成了一個關於\(i\)的向量與一個固定向量的叉積。
由於叉積的最大值一定在凸包上,我們只要維護一個凸殼然後每次在其上二分即可。
分塊維護凸殼
由於叉積是滿足分配律的,我們可以用分塊來維護,定義新的\(\vec{p_i}\)僅表示這個塊內的字首和。
這樣一來,單點修改就只要重構一個塊的凸殼就好了。
詢問的時候我們列舉每個塊,在這個塊的凸殼上二分,給二分結果加上前面塊之和與\(S\)
複雜度為\(O(q(S+\frac nSlog S))\),取\(S=\sqrt{nlogn}\)得到最優複雜度\(O(nq\sqrt{nlogn})\)。
程式碼:\(O(nq\sqrt{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('\n');} }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; }