• 洛谷 P4272


    洛谷题面传送门

    u1s1 在我完成这篇题解之前,全网总共两篇题解,一篇使用的平衡树,一篇使用的就是这篇题解讲解的这个做法,但特判掉了一个点,把特判去掉在 BZOJ 上会 WA 一个点。

    两篇题解都异常简略,前一篇题解甚至只有代码没有说明,所以这里我来写篇比较详细题解造福人类了(大雾

    建议没做过这题的同学先看看这道题,看题解区第一个做法,对这题有比较大的启发。

    注:下文中分别用 (a,b,l,r) 代替题面中的 (X,Y,A,B)

    首先关于这题咱们可以想到一个非常 naive 的 DP,(dp_{i,j}) 表示考虑到前两个数,(b_i=j)(sumlimits_{k=1}^i|a_k-b_k|)。那么显然有 DP 转移 (dp_{i,j}=minlimits_{k=l}^rdp_{i-1,j-k}+|a_i-j|)。一脸不好直接维护的样子。不过注意到绝对值函数是一个下凸函数,因此我们猜测对于一个固定的 (i)(dp_{i,j})(j)​ 的增大也是一个下凸函数,事实也的确如此,证明大概可以归纳,可能需要一些分类讨论,这里不再赘述,留给读者自己思考。

    我们还可以注意到,每一轮中每条直线斜率变化的绝对值最多为 (1)​​,也就是说任意时刻,下凸壳中直线斜率绝对值的最大值顶多为 (n)​​,因此我们考虑维护 (2n+2)​​ 个分界点的横坐标,具体来说,记 (x_i)​​ 为第 (i)​​ 个分界点,那么以凸包上横坐标分别为 (x_i,x_{i+1})​​ 的点组成线段刚好是凸包上斜率为 (i-n-2)​​ 的直线,当然也有可能 (x_i=x_{i+1})​​,此时凸包上不存在斜率为 (i-n-2)(说白了就是记录凸包拐点的横坐标)。

    接下来考虑加入一个 (a_i)​​ 后会对凸包产生怎样的影响。我们考虑将每一轮转移的过程分成两部分:(dp_{i,j}=minlimits_{k=l}^rdp_{i-1,j-k})​ 和 (dp_{i,j}:=dp_{i,j}+|a_i-j|)​,首先对于第一部分而言,显然在凸包上存在一些分界点,满足分界点前面单调不增,分界点之后单调不降,不难发现这些分界点组成的集合就是斜率为 (0)​ 的直线。假设斜率为 (0)​ 的直线的两个端点横坐标为 (u,v(u,v))​,那么不难发现对于 (jle u+l),由于 (u) 前面单调递减,因此我们肯定会尽量选择 (j) 与接近的点转移,即 (dp_{i,j-l}),因此我们可以得到,进行第一步操作后,凸包上 (u) 前面的点都会向右平移 (l) 格,同理在 (v+r) 后面的点 (j),我们肯定会尽量选离 (j) 远的点的即 (j-r),也就是说 (v)​ 后面的点肯定会向右平移 (r) 格,至于中间的部分……那显然还是一条水平的直线咯(

    第二部分就相对比较简单了,不难发现 (f(x)=|a_i-x|)((-infty,a_i)) 中是斜率为 (-1) 的直线,((a_i,infty))​ 中是斜率为 (1) 的直线,因此 (a_i)​ 前面的部分的直线的斜率会减 (1),后面的部分斜率会加 (1)。这样我们可以看作是在 (a_i) 处插入了两个断点。

    考虑怎样维护这个东西,我们开两个堆 (L,R) 分别存储斜率为 (0) 的直线前面和后面的断点,那么时刻 (i) 时显然 (L) 应当为存储的是最小的 (i) 个断点,(R) 应当存储最大的 (i) 个断点,但有时并不一定真的存储的就是最小/最大的 (i) 个断点,这时候我们就要进行调整,具体方法就是取出 (L) 中最大的元素 (x) 和最小的元素 (y),如果 (x>y) 就将 (x) 插入 (R)(y) 插入 (L),否则 break。还有一个问题就是如何处理坐标平移,具体方法就在 (i) 时刻是将 (x) 插入 (L) 时我们不插入 (x) 本身,而是插入 (x-(i-1)l),这样在 (i’) 时刻这个点真正的坐标就是 (x+i’l),不难发现这里用了差分的思想,在统计每个点有多少个时刻满足 xxxx 条件时也用到了类似的思想。

    时间复杂度 (nlog n)

    注意加一些特判,否则在 BZOJ 上会 WA 一个点,进而取得 (0) 分的好成绩(London Fog

    const int MAXN=5e5;
    int n,mx,l,r,a[MAXN+5],b[MAXN+5];ll ext=0;
    priority_queue<ll> L;
    priority_queue<ll,vector<ll>,greater<ll> > R;
    //void prt(auto q){
    //	while(!q.empty()) printf("%d ",q.top()),q.pop();
    //	printf("
    ");
    //}
    int main(){
    	scanf("%d%d%d%d",&n,&mx,&l,&r);
    	for(int i=1;i<=n;i++){
    		scanf("%d",&a[i]);
    		if(a[i]<1ll*(i-1)*l+1){
    			ext+=abs(a[i]-(1ll*(i-1)*l+1));
    			a[i]=1ll*(i-1)*l+1;
    		}
    	}
    	for(int i=1;i<=n;i++){
    		L.push(a[i]-1ll*(i-1)*l);
    		R.push(a[i]-1ll*(i-1)*r);
    		while(1){
    			ll x=L.top();x+=1ll*(i-1)*l;
    			ll y=R.top();y+=1ll*(i-1)*r;
    //			printf("%lld %lld
    ",x,y);
    			if(x<=y) break;L.pop();R.pop();
    			L.push(y-1ll*(i-1)*l);R.push(x-1ll*(i-1)*r);
    		} b[i]=L.top()+1ll*(i-1)*l;
    //		prt(L);prt(R);
    	} ll res=0;chkmin(b[n],mx);
        chkmax(b[n],1ll*l*(n-1)+1);//be careful
    	for(int i=n-1;i;i--){
    		if(b[i]<b[i+1]-r) b[i]=b[i+1]-r;
    		if(b[i]>b[i+1]-l) b[i]=b[i+1]-l;
    	}
    //	if(n==8888) for(int i=1;i<=n;i++) printf("%d%c",b[i]," 
    "[i==n]);
    	for(int i=1;i<=n;i++) res+=abs(a[i]-b[i]);
    	printf("%lld
    ",res+ext);
    	return 0;
    }
    
  • 相关阅读:
    Linux 服务器配置网站以及绑定域名
    vmware 下的三种网络模式
    排序算法-python版
    python实例2-写一个爬虫下载小功能
    python学习-1-列表,元组,字典,集合,文件操作
    linux 10 -Bash Shell编程
    [ActionScript 3.0] AS3 3D双圆环贴图
    [ActionScript 3.0] AS3 3D星形贴图
    [ActionScript 3.0] AS3 绘制星形
    [ActionScript 3.0] AS3 获取函数参数个数
  • 原文地址:https://www.cnblogs.com/ET2006/p/luogu-P4272.html
Copyright © 2020-2023  润新知