• 浅谈代数法斜率优化


    我一般采用的方法是 截距式优化。

    更快速 更正轨的是采用代数法进行斜率优化 这种方法 使用较多且使用范围广泛。

    注意:斜率优化和决策单调性 含义不相同 决策单调性一般是四边形不等式优化出来的结果。

    而斜率优化只是在解决一些特定问题寻找最优决策时的优化。

    通常题目可能可以同时进行斜率优化或决策单调性优化 但决策单调性优化有下界复杂度Qlog 而斜率优化如果斜率单调通常可以使O(Q)的。

    LINK:CF631E Product Sum

    对于一个数列我们定义其代价为 (sum_{i=1}{n}icdot a_i)

    可以将某个i交换到j 使得j之后的数字往后推形成的新的序列最大。

    显然我们可以(n^2)暴力之后O(1)算出贡献。但是这不够优秀。

    i可以向前也可以向后 我们先考虑向前的方向。显然设f[i]表示i和1~i这些位置中的一个交换得到的最大价值。

    设原本的初始值为m.那么f[i]可以表示 换过后的值和初始值的差。

    (f_i=(j-i)cdot a_i+sum_{i-1}-sum_{j-1})

    这个时候一般是要对比一下两个决策 再来一个决策k (1leq k<j)

    (f_i=(k-i)cdot a_i+sum_{i-1}-sum_{k-1})

    我们可以让他们来一个 大小关系的比较看能得出什么 假设j决策优于k决策。

    显然有 ((j-i)cdot a_i+sum_{i-1}-sum_{j-1}geq (k-i)cdot a_i+sum_{i-1}-sum_{k-1})

    化简一下 ((j-k)cdot a_igeq sum_{j-1}-sum_{k-1} o a_igeq frac{sum_{j-1}-sum_{k-1}}{j-k})

    到这里 可以发现右边 神似我们的斜率式子 也就是 在一个二维坐标系中我们以w为下标 (sum_{w-1})为总坐标

    右边其实是我们坐标系中的一个两点的斜率 也同样说明了 如果我们的(a_i)大于这个斜率说明了j是比k优的的。

    此时 如果只有一个斜率没的说比一下即可 有三个点的时候 我们可以来分析一下他们之间的关系 我分析的话过于抽象 可以自己分析 j1,j2,j3看一下他们有什么关系。

    可以发现上凸包一定不优策略 我们删掉一些点形成下凸 斜率单调递增。

    接下来直接在凸包上线性规划即可 由于这道题 ai不单调 所以需要在凸包上二分。

    反着的过程也同样如此。

    其实 代数法优化的优越性 这道题没体现出来 但是简洁性还是很棒的。

    const ll MAXN=200010;
    ll n,w,l,r,cnt;
    ll a[MAXN],f[MAXN],sum[MAXN];
    ll q[MAXN];
    inline db slope(ll x,ll y){return 1.0*(sum[y-1]-sum[x-1])/(y-x);}
    inline db slope1(ll x,ll y){return 1.0*(sum[y]-sum[x])/(y-x);}
    inline ll find(ll x)
    {
    	if(r==1)return q[l];
    	ll L=1,R=r;
    	while(L+1<R)
    	{
    		ll mid=(L+R)>>1;
    		if(slope(q[mid-1],q[mid])>=x)R=mid;
    		else L=mid;
    	}
    	if(slope(q[L],q[R])>=x)return q[L];
    	return q[R];
    }
    inline ll find2(ll x)
    {
    	if(r==1)return q[l];
    	ll L=1,R=r;
    	while(L+1<R)
    	{
    		ll mid=(L+R)>>1;
    		if(slope1(q[mid-1],q[mid])>=x)L=mid;
    		else R=mid;
    	}
    	if(slope1(q[L],q[R])>=x)return q[R];
    	return q[L];
    }
    int main()	
    {
    	freopen("1.in","r",stdin);
    	get(n);
    	rep(1,n,i)get(a[i]),w+=a[i]*i,sum[i]=sum[i-1]+a[i];
    	q[l=r=1]=1;
    	rep(2,n,i)
    	{
    		ll w=find(a[i]);//二分凸包
    		f[i]=(w-i)*a[i]+sum[i-1]-sum[w-1];
    		while(l<r&&slope(q[r],i)<=slope(q[r-1],q[r]))--r;
    		q[++r]=i;cnt=max(cnt,f[i]);
    	}
    	//f[i]= (j-i)*ai-sum[j]+sum[i];
    	q[l=r=1]=n;
    	for(ll i=n-1;i>=1;--i)
    	{
    		ll w=find2(a[i]);
    		f[i]=(w-i)*a[i]-sum[w]+sum[i];
    		while(l<r&&slope1(q[r],i)>=slope1(q[r-1],q[r]))--r;
    		q[++r]=i;cnt=max(cnt,f[i]);
    	}
    	printf("%lld
    ",cnt+w);
    	return 0;
    }
    

    这道题并不算简单 反着的时候很容易迷 建议反着的时候画画图再写。
    记得我上次写反着的时候是再模拟赛上CDQ分治套斜率优化 迷死我了 然后写+调了快3h 结果崩盘了

  • 相关阅读:
    Windows Phone 播放音频之MediaPlayer
    (转)系统架构师学习笔记_第四章(下)
    (转)系统架构师学习笔记_第二章
    (转)系统架构师学习笔记_第四章(上)
    读取properties配置文件信息
    (转)系统架构师学习笔记_第五章(上)
    (转)系统架构师学习笔记_第三章
    Struts2实现文件上传
    Struts2手工编写代码实现对Action中所有方法输入校验
    (转)系统架构师学习笔记_第五章(下)
  • 原文地址:https://www.cnblogs.com/chdy/p/12503060.html
Copyright © 2020-2023  润新知