zoukankan      html  css  js  c++  java
  • 【洛谷7028】[NWRRC2017] Joker(分块+凸壳)

    点此看题面

    • 一个长度为(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;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    FastReport合并多份报表为一份预览打印
    Delphi使用AcroPDF ActiveX显示PDF文件
    Delphi使用Zxing创建二维码
    TreeView和ListView数据库查询数据联动操作
    根据数据库查询结果动态创建控件(仿看板模式显示)
    GridView控件使用
    LayoutControl控件使用
    TreeListLookUpEdit控件使用
    Devexpress TreeList控件使用
    Asp.Net 5上传文件 (Core API方式)
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu7028.html
Copyright © 2011-2022 走看看