• YbtOJ#493最大分数【斜率优化dp,分治】


    正题

    题目链接:http://www.ybtoj.com.cn/contest/117/problem/1


    题目大意

    \(n\)个数的一个序列,给其中的一些数打上标记。
    一个标记方案的贡献为\(s_1\)表示有多少对\(L,R\)满足区间\([L,R]\)都被打上了标记,\(s_2\)表示标记的数字和。贡献为\(s_1-s_2\)

    \(m\)次询问,修改一个数后,求最大的可能贡献(询问之间相互独立)

    \(1\leq n,m\leq 3\times 10^5\)


    解题思路

    先把答案减去一个\(\sum_{i=1}^na_i\),这样就变为对于一段\([L,R]\)要么选择\(s_1\)要么选择\(s_2\)
    先考虑不带修改怎么搞,设\(f_i\)表示前\(i\)个的最大贡献。

    定义\(s_i=\sum_{j=1}^ia_i\)然后有方程

    \[f_i=max\{f_j+max\{s_i-s_j,\binom{i-j}{2}\}\} \]

    这个东西可以斜率优化搞(考场上犯病写了个\(CDQ\),调了我半天)

    然后带修改,考虑到每次只会影响一个数字,可以理解为一个前缀和后缀拼起来。把刚刚那个\(dp\)再反过来做一次记为\(g_i\)

    那么现在对于修改的位置\(x\),我们要么选择\(x\),要么跨过\(x\),也就是最大化

    \[max\{f_j+g_i+s_{i-1}-s_j,f_j+g_i+\binom{i-j-1}{2}\}(i\in[0,x-1],j\in[x+1,n+1]) \]

    前面那个可以把两个前/后缀的max的\(f_j-s_j\)\(g_i+s_{i-1}\)加起来就好了。

    后面那个考虑分治,我们每次分治到一个位置\([l,mid,r]\),如果需要往左走就统计\(i\in[0,l-1]\)\(j\in[mid+1,r]\)的答案。右边同理。

    因为我们的每次的时间复杂度是外面的大小,所以我们可以维护一个前后缀的凸壳,然后在上面二分时间复杂度就是\(O(n\log^2 n)\)的。

    同时维护两个凸壳需要回溯所以很麻烦,分开两次正反做一次就好了。

    时间复杂度\(O(n\log^2 n)\)


    code

    (考场代码,有点丑)

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<vector>
    #define ll long long
    using namespace std;
    const ll N=3e5+10,inf=1e18;
    ll n,m,ans,lmax,rmax,top,st[N],suf[N];
    ll a[N],f[N],g[N],s[N],prt[N],w[N],yf[N];
    vector<ll > q[N];
    ll px(ll x,ll *f)
    {return f[x]*2+x*x-x;}
    ll xj(ll x1,ll y1,ll x2,ll y2)
    {return x1*y2-x2*y1;}
    ll xl(ll a,ll b,ll c,ll *f){
    	ll y1=px(b,f)-px(a,f),x1=b-a;
    	ll y2=px(c,f)-px(a,f),x2=c-a;
    	return xj(x1,y1,x2,y2);
    }
    ll ck(ll l,ll r,ll *f)
    {return f[l]+(r-l+1)*(r-l)/2;}
    ll solve(ll l,ll r,ll *f){
    	if(l==r)return f[l]-s[l];
    	ll mid=(l+r)>>1;
    	ll maxs=solve(l,mid,f);top=0;
    	for(ll i=l;i<=mid;i++){
    		while(top>1&&xl(st[top-1],st[top],i,f)>=0)top--;
    		st[++top]=i;
    	}
    	for(ll i=mid+1;i<=r;i++){
    		while(top>1&&ck(st[top],i,f)<ck(st[top-1],i,f))top--;
    		f[i]=max(f[i],max(ck(st[top],i,f),s[i]+maxs));
    	}
    	maxs=max(maxs,solve(mid+1,r,f));
    	return maxs;
    }
    ll cw(ll l,ll r)
    {return f[l]+g[r]+(r-l-1)*(r-l)/2;}
    ll xll(ll a,ll b,ll c){
    	ll y1=(yf[b]-yf[a])*2,x1=b-a;
    	ll y2=(yf[c]-yf[a])*2,x2=c-a;
    	return xj(x1,y1,x2,y2);
    }
    ll xlr(ll a,ll b,ll c){
    	ll y1=yf[b]-yf[a],x1=(n-b+1)-(n-a+1);
    	ll y2=yf[c]-yf[a],x2=(n-c+1)-(n-a+1);
    	return xj(x1,y1,x2,y2);
    }
    void calc(ll L,ll R){
    	if(L==R){
    		for(ll i=0;i<q[L].size();i++){
    			ll x=q[L][i],tmp;
    			prt[x]=max(ans-w[x]+a[L]-s[n],prt[x]);
    		}
    		while(top>1&&xll(st[top-1],st[top],L)>=0)top--;
    		st[++top]=L;
    		return;
    	}
    	ll mid=(L+R)>>1,pa=ans;
    	if(top){
    		for(ll i=mid+1;i<=R;i++){
    				ll l=1,r=top-1;
    				while(l<=r){
    					ll x=(l+r)>>1;
    					if(cw(st[x],i)<=cw(st[x+1],i))l=x+1;
    					else r=x-1;
    				}
    				ans=max(ans,cw(st[l],i));
    		}
    	}
    	calc(L,mid);ans=pa;
    	calc(mid+1,R);
    	return;
    }
    void cblc(ll L,ll R){
    	if(L==R){
    		for(ll i=0;i<q[L].size();i++){
    			ll x=q[L][i],tmp;
    			prt[x]=max(ans-w[x]+a[L]-s[n],prt[x]);
    		}
    		while(top>1&&xlr(st[top-1],st[top],L)>=0)top--;
    		st[++top]=L;
    		return;
    	}
    	ll mid=(L+R)>>1,pa=ans;
    	if(top){
    		for(ll i=L;i<=mid;i++){
    				ll l=1,r=top-1;
    				while(l<=r){
    					ll x=(l+r)>>1;
    					if(cw(i,st[x])<=cw(i,st[x+1]))l=x+1;
    					else r=x-1;
    				}
    				ans=max(ans,cw(i,st[l]));
    		}
    	}
    	cblc(mid+1,R);ans=pa;
    	cblc(L,mid);
    	return;
    }
    signed main()
    {
    	freopen("score.in","r",stdin);
    	freopen("score.out","w",stdout);
    	scanf("%lld",&n);
    	for(ll i=1;i<=n;i++)
    		scanf("%lld",&a[n-i+1]);
    	for(ll i=1;i<=n;i++)s[i]=s[i-1]+a[i];
    	solve(0,n,g);
    	for(ll i=1;i<n-i+1;i++)
    		swap(a[i],a[n-i+1]),swap(g[i],g[n-i+1]);
    	for(ll i=1;i<=n;i++)s[i]=s[i-1]+a[i];
    	solve(0,n,f);
    	scanf("%lld",&m);
    	for(ll i=1;i<=m;i++){
    		ll x;
    		scanf("%lld%lld",&x,&w[i]);
    		q[x].push_back(i);
    	}
    	
    	suf[n+2]=-inf;
    	for(ll i=n+1;i>=1;i--)
    		suf[i]=max(suf[i+1],g[i]+s[i-1]);
    	for(ll i=0,pre=-inf;i<=n;i++){
    		for(ll j=0;j<q[i].size();j++){
    			ll x=q[i][j];
    			prt[x]=suf[i+1]+pre-s[n];
    		}
    		pre=max(pre,f[i]-s[i]);
    	}
    	
    	ans=-inf;top=0;
    	for(ll i=1;i<=n;i++)yf[i]=f[i]*2+i*i+i;calc(0,n+1);
    	ans=-inf;top=0;
    	for(ll i=1;i<=n;i++)yf[i]=g[i]*2+(n-i+1)*(n-i+1)+(n-i+1);
    	cblc(0,n+1);
    	for(ll i=1;i<=m;i++)
    		printf("%lld\n",max(prt[i],0ll));
    	return 0;
    }
    
  • 相关阅读:
    Java中的软(弱)引用
    国庆节前夕的夜晚
    Internet连接共享只能上qq不能打开网页的问题解决
    Android多线程研究(9)——读写锁
    JavaScript开发——文件夹的上传和下载
    JS开发——文件夹的上传和下载
    B/S开发——文件夹的上传和下载
    php web开发——文件夹的上传和下载
    asp.net web开发——文件夹的上传和下载
    Java web开发——文件夹的上传和下载
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/14412512.html
Copyright © 2020-2023  润新知