1 条题解

  • 0
    @ 2025-8-24 21:49:44

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar HoshiuZ
    生命潦草,我在弯腰。

    搬运于2025-08-24 21:49:43,当前版本为作者最后更新于2020-02-24 11:27:46,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    w(x,y)w(x,y)是定义在整数集合上的二院函数。若对于定义域上的任意整数a,b,c,da,b,c,d,其中abcda\le b\le c\le d,都有w(a,d)+w(b,c)w(a,c)+w(b,d)w(a,d)+w(b,c)\ge w(a,c)+w(b,d)成立,则称函数ww满足四边形不等式

    定理

    w(x,y)w(x,y)是定义在整数集合上的二元函数。若对于定义域上的任意整数a,ba,b,其中a<ba<b,都有w(a,b+1)+w(a+1,b)w(a,b)+w(a+1,b+1)w(a,b+1)+w(a+1,b)\ge w(a,b)+w(a+1,b+1)成立,则函数ww满足四边形不等式。

    证明

    对于a<ca<c,有w(a,c+1)+w(a+1,c)w(a,c)+w(a+1,c+1)w(a,c+1)+w(a+1,c)\ge w(a,c)+w(a+1,c+1)

    对于a+1<ca+1<c,有w(a+1,c+1)+w(a+2,c)w(a+1,c)+w(a+2,c+1)w(a+1,c+1)+w(a+2,c)\ge w(a+1,c)+w(a+2,c+1)

    两式相加,得到w(a,c+1)+w(a+2,c)w(a,c)+w(a+2,c+1)w(a,c+1)+w(a+2,c)\ge w(a,c)+w(a+2,c+1)

    以此类推,对于任意的abca\le b\le c,有w(a,c+1)+w(b,c)w(a,c)+w(b,c+1)w(a,c+1)+w(b,c)\ge w(a,c)+w(b,c+1)

    同理,对任意的abcda\le b\le c\le d,有w(a,d)+w(b,c)w(a,c)+w(b,d)w(a,d)+w(b,c)\ge w(a,c)+w(b,d)

    决策单调性

    在状转方程f[i]=min{f[j]+w(j,i)},j[0,i)f[i]=\min\{f[j]+w(j,i)\},j\in[0,i)中,若函数ww满足四边形不等式,则ff具有决策单调性。

    证明

    定义p[i]p[i]表示ii的最优决策点。

    i[1,n],j[0,p[i]1]\forall i\in[1,n],\forall j\in[0,p[i]-1],根据p[i]p[i]ii的最优决策点,则有

    f[p[i]]+w(p[i],i)f[j]+w(j,i)f[p[i]]+w(p[i],i)\le f[j]+w(j,i)

    i[i+1,n]\forall i'\in[i+1,n],因为ww满足四边形不等式,则有

    w(j,i)+w(p[i],i)w(j,i)+w(p[i],i)w(j,i')+w(p[i],i)\ge w(j,i)+w(p[i],i')

    移项,可得

    v(p[i],i)w(p[i],i)w(j,i)w(j,i)v(p[i],i')-w(p[i],i)\le w(j,i')-w(j,i)

    与第一个不等式相加,可得

    f[p[i]]+w(p[i],i)f[j]+w(j,i)f[p[i]]+w(p[i],i')\le f[j]+w(j,i')

    ii'的最优决策点一定大于等于p[i]p[i]。故ff具有决策单调性。

    应用

    那么知道其有决策单调性后,有什么应用呢?

    既然其有决策单调性,定义p[i]p[i]表示ii的最优决策点,则pp内一定是非严格单调递增的。

    那么根据此性质,满足决策单调性的题目主要有两种处理手法。

    方法一:单调队列

    可以用单调队列维护决策集合pp

    当求出一个新的f[i]f[i]时,考虑ii可以作为哪些f[i] (i>i)f[i']\ (i'>i)的最优决策。根据决策单调性,我们一定可以找到一个位置,在该位置之前,pp内存储的决策都比ii要优,其后都比ii差。于是便可以将该位置及其后面的部分全部变为ii,即此时它们的最优决策均为ii

    一个位置一个位置的修改效率很低,所以可以建立一个队列,代替pp。队列中保存三个量(j,l,r)(j,l,r),表示p[lp[l~r]r]的值都是jj

    对于每个i[1,n]i\in[1,n],执行以下操作:

    1. 检查队头:设队头为(j0,l0,r0)(j_0,l_0,r_0),若r0=i1r_0=i-1,则删除队头(因为队头不需要了,f[i]f[i]之前的值已经被求出)。否则则令l0=il_0=i

    2. 取队头保存的最优决策jj进行状态转移,计算出f[i]f[i]

    3. 尝试插入新决策ii,步骤如下

      (1) 取出队尾,即为(jt,lt,rt)(j_t,l_t,r_t)

      (2) 若对于f[lt]f[l_t]来说,ii是比jtj_t更优的决策,即f[i]+w(i,lt)f[jt]+w(jt,lt)f[i]+w(i,l_t)\le f[j_t]+w(j_t,l_t),记pos=ltpos=l_t,删除队尾,返回步骤(1)。

      (3) 若对于f[rt]f[r_t]来说,ii是比jtj_t更优的决策,即f[jt]+w(jt,rt)f[i]+w(i,rt)f[j_t]+w(j_t,r_t)\le f[i]+w(i,r_t),去往步骤(5)。

      (4) 否则,则在[lt,rt][l_t,r_t]上二分查找出位置pospos,在此之前决策比ii优,在此之后决策ii更优,将[lt,rt][l_t,r_t]修改为[lt,pos1][l_t,pos-1],去往步骤(5)。

      (5) 把三元组(i,pos,n)(i,pos,n)插入队尾。

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

    方法二:分治

    假设已知[l,r][l,r]的最优决策在[L,R][L,R]上。

    定义mid=l+r2mid=\frac{l+r}{2},设dp[mid]dp[mid]的最优决策为pp,根据决策单调性,可知[l,mid1][l,mid-1]的最优决策在[L,p][L,p]内,[mid+1,r][mid+1,r]的最优决策在[p,R][p,R]内。

    于是将问题分割成了同类型的规模更小的问题,便可递归分治。

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

    例题 [POI2011]Lightning Conductor

    给定一个长度为n n的序列{an}\{a_n\},对于每个i[1,n]i\in [1,n],求出一个最小的非负整数pp,使得 j[1,n]\forall j\in[1,n],都有ajai+pija_j\le a_i+p-\sqrt{|i-j|}

    数据范围

    1n5×1051 \le n \le 5\times 10^{5}0ai1090 \le a_i \le 10^{9}

    链接

    [POI2011]Lightning Conductor

    思路

    ajai+pij,j[1,n]a_j \le a_i+p-\sqrt{|i-j|},\forall j\in[1,n] paj+ijai,j[1,n]p \ge a_j+\sqrt{|i-j|}-a_i,\forall j\in[1,n]

    pp为非负整数,则

    $$p=\lceil \max\{ a_j+\sqrt{|i-j|}\}\rceil-a_i,\forall j\in[1,n] $$

    那么问题便转变为求$\lceil \max\{ a_j+\sqrt{|i-j|}\}\rceil,\forall j\in[1,n]$。

    可以考虑做两次,正做一次,将序列翻转再做一次,结果取两次的最大值,这样便可以去掉绝对值

    $$\lceil \max\{ a_j+\sqrt{i-j}\}\rceil,\forall j\in[1,i) $$

    定义dp[i]=max{aj+ij}dp[i]=\lceil \max\{ a_j+\sqrt{i-j}\}\rceil

    此处的w(j,i)w(j,i)即为ij\sqrt{i-j}

    定义a+1<ca+1<c

    w(a,c)=caw(a,c)=\sqrt{c-a} w(a+1,c)=ca1w(a+1,c)=\sqrt{c-a-1} w(a,c+1)=ca+1w(a,c+1)=\sqrt{c-a+1} w(a+1,c+1)=caw(a+1,c+1)=\sqrt{c-a}

    $$w(a,c+1)+w(a+1,c)-[w(a,c)+w(a+1,c+1)]=\sqrt{c-a-1}+\sqrt{c-a+1}-2\sqrt{c-a} $$

    d=cad=c-a,原式变为

    $$\sqrt{d-1}+\sqrt{d+1}-2\sqrt{d}=(\sqrt{d+1}-\sqrt{d})-(\sqrt{d}-\sqrt{d-1}) $$

    而函数f(x)=xx1f(x)=\sqrt{x}-\sqrt{x-1}单调递减

    则该式恒小于00

    w(a,c+1)+w(a+1,c)w(a,c)+w(a+1,c+1)w(a,c+1)+w(a+1,c)\le w(a,c)+w(a+1,c+1)

    可推广到对于abcda\le b\le c\le dw(a,d)+w(b,c)w(a,c)+w(b,d)w(a,d)+w(b,c) \le w(a,c)+w(b,d)

    可以发现,这东西与四边形不等式的符号相反。将上面的证明过程符号取反(因为本题求的是max\max),便可证明dpdp满足决策单调性。

    于是采用上面两种方法实现即可。

    由于函数sqrtsqrt较慢,且本题反复调用,可提前预处理出1\sqrt{1}~n\sqrt{n}

    代码(单调队列)

    #include<bits/stdc++.h>
    #define N 500010
    
    using namespace std;
    
    int n,head,tail,a[N];
    double dp[N],sqr[N];
    struct node{
    	int l,r,p;
    }q[N];
    
    double w(int j,int i) {
    	return double(a[j])+sqr[i-j];
    }
    
    int binary_search(int t,int x) {
    	int ans=q[t].r+1,l=q[t].l,r=q[t].r;
    	while(l<=r) {
    		int mid=l+r>>1;
    		if(w(q[t].p,mid)<=w(x,mid)) ans=mid,r=mid-1;
    		else l=mid+1;
    	}
    	return ans;
    }
    
    void insert(int i) {
    	q[tail].l=max(q[tail].l,i);
    	while(head<=tail&&w(i,q[tail].l)>=w(q[tail].p,q[tail].l)) tail--;
    	if(head>tail) {
    		q[++tail].l=i;
    		q[tail].r=n;
    		q[tail].p=i;
    	}
    	else {
    		int pos=binary_search(tail,i);
    		if(pos>n) return ;
    		q[tail].r=pos-1;
    		q[++tail].l=pos;
    		q[tail].r=n;
    		q[tail].p=i;
    	}
    }
    
    void work() {
    	head=1,tail=0;
    	for(int i=1;i<=n;i++) {
    		insert(i);
    		if(head<=tail&&q[head].r<i) head++;
    		else q[head].l=i;
    		dp[i]=max(dp[i],w(q[head].p,i));
    	}
    }
    
    int main() {
    	cin>>n;
    	for(int i=1;i<=n;i++) {
    		cin>>a[i];
    		sqr[i]=sqrt(i);
    	}
    	
    	work();
    	for(int i=1;i<=n/2;i++) swap(a[i],a[n-i+1]),swap(dp[i],dp[n-i+1]);
    	work();
    	
    	for(int i=n;i>=1;i--) cout<<(int)ceil(dp[i])-a[i]<<endl;
    	
    	return 0;
    }
    

    代码(分治)

    #include<bits/stdc++.h>
    #define N 500010
    
    using namespace std;
    
    int n,a[N];
    double dp[N],sqr[N];
    
    double w(int j,int i) {
    	return double(a[j])+sqr[i-j];
    }
    
    void work(int l,int r,int L,int R) {
    	if(l>r) return ;
    	int mid=l+r>>1,p;
    	double maxn=0;
    	for(int i=L;i<=min(mid,R);i++) {
    		if(w(i,mid)>maxn) maxn=w(i,mid),p=i;
    	}
    	dp[mid]=max(dp[mid],maxn);
    	work(l,mid-1,L,p);
    	work(mid+1,r,p,R);
    }
    
    int main() {
    	cin>>n;
    	for(int i=1;i<=n;i++) {
    		cin>>a[i];
    		sqr[i]=sqrt(i);
    	}
    	
    	work(1,n,1,n);
    	for(int i=1;i<=n/2;i++) swap(a[i],a[n-i+1]),swap(dp[i],dp[n-i+1]);
    	work(1,n,1,n);
    	
    	for(int i=n;i>=1;i--) cout<<(int)ceil(dp[i])-a[i]<<endl;
    	
    	return 0;
    }
    

    参考资料

    《算法竞赛进阶指南》

    【学习笔记】动态规划—各种 DP 优化

    • 1

    信息

    ID
    2591
    时间
    1000ms
    内存
    125MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者