四边形不等式

发布时间 2023-08-06 20:22:07作者: SError

写的有点答辩了。

四边形不等式优化

最简单的一种:

2D1D的状态转移方程:

\[f_{l,r}=\min_{k=l}^{r-1}\{f_{l,k}+f_{k+1,r}\}+w(l,r) \]

\(w(l,r)\) 满足一些性质时能利用决策单调性优化。

  • 区间包含单调性\(\forall\space l\le l'\le r'\le r,w(l',r')\le w(l,r)\) ,那么 \(w\) 对于区间包含关系具有单调性。

  • 四边形不等式\(\forall\space l_1\le l_2\le r_1\le r_2,w(l_1,r_1)+w(l_2,r_2)\le w(l_1,r_2)+w(l_2,r_1)\) (交叉小于包含),则 \(w\) 满足四边形不等式。等号恒成立称 \(w\) 满足 四边形恒等式


  • 若对于定义域上的任意整数 \(a<b\),有 \(w(a,b+1)+w(a+1,b)\ge w(a,b)+w(a+1,b+1)\),则 \(w\) 满足四边形不等式。

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

对于 \(a+1<c\),有 \(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)\ge w(a,c)+w(a+2,c+1)\)

即对于任意 \(a\le b\le c\),有 \(w(a,c+1)+w(b,c)\ge w(a,c)+w(b,c+1)\) .

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


  • \(w(l,r)\) 满足区间包含单调性和四边形不等式,则 \(f(l,r)\) 满足四边形不等式。

太长了不放,感性理解。


  • 若状态 \(f\) 满足四边形不等式,记 \(m_{l,r}=\min\{k:f_{l,r}=g_{k,l,r}\}\) 为最优决策点,则

\[m_{l,r-1}\le m_{l,r}\le m_{l+1,r} \space (l+1<r) \]

\(u=m_{l,r},k_1=m_{l,r-1},k_2=m_{l+1,r}\) ,即证明 \(k_1\le u\le k_2\) .

\(1.\)\(k_1>u\) ,则 \(u+1\le k_1+1\le r-1\le r\) ,有

\[f_{u+1,r-1}+f_{k_1+1,r}\le f_{u+1,r}+f_{k_1+1,r-1} \]

因为 \(u\)\(f_{l,r}\) 的最优决策点:

\[f_{l,u}+f_{u+1,r}\le f_{l,k_1}+f_{k_1+1,r} \]

两式相加可得

\[f_{l,u}+f_{u+1,r-1}\le f_{l,k_1}+f_{k_1+1,r-1} \]

\(g_{u,l,r-1}\le g_{k_1,l,r-1}\) ,那么 \(u=m_{l,r-1}\) ,与 \(k_1>u\) 矛盾。

所以 \(k_1\le u\) .

\(2.\)\(u>k_2\) ,则 \(l\le l+1\le k_2\le u\) ,有

\[f_{l,k_2}+f_{l+1,u}\le f_{l,u}+f_{l+1,k_2} \]

因为 \(k_2\)\(f_{l+1,r}\) 的最优决策点:

\[f_{l+1,k_2}+f_{k_2+1,r}\le f_{l+1,u}+f_{u+1,r} \]

两式相加可得

\[f_{l,k_2}+f_{k_2+1,r}\le f_{l,u}+f_{u+1,r} \]

\(g_{k_2,l,r}\le g_{u,l,r}\) ,那么 \(k_2=m_{l,r}\) ,与 \(u>k_2\) 矛盾。

所以 \(u\le k_2\) .

综上可得 \(k_1\le u\le k_2\) ,即 \(m_{l,r-1}\le m_{l,r}\le m_{l+1,r}\) .


得到决策单调性就可以进行DP了。

当计算 \(f_{l,r}\) 时,将其最优决策点 \(m_{l,r}\) 记录下来,那么对决策点的枚举总量为

\[\sum_{1\le l\le r\le n}m_{l+1,r}-m_{l,r-1}=\sum_{i=1}^{n}m_{i,n}-m_{1,i}\le n^2 \]

长这个样子。

for(int len=2;len<=n;len++)
		for(int l=1,r=len;r<=n;l++,r++){
			f[l][r]=inf;
			for(int k=m[l][r-1];k<=m[l+1][r];k++)
				if(f[l][r]>f[l][k]+f[k+1][r]+w(l,r)){
					f[l][r]=f[l][k]+f[k+1][r]+w(l,r);
					m[l][r]=k;
				}
		}

写了没保存。

P4767 [IOI2000]邮局

数轴上 \(n\) 个点 \(\{a\}\) ,放 \(m\) 个点,最小化

\[\sum_{i=1}^{n}\min_{j=1}^{m}|a_i-b_j| \]

\(n\le 3000,m\le300\) .

先将 \(a\) 升序排序。

\(f(i,j)\) 表示考虑前 \(i\) 个点,放置 \(j\) 个点的最小值。

那么

\[f(i,j)=\min_{k=0}^{i-1}f(k,j-1)+w(k+1,i) \]

其中 \(w(l,r)\) 指在这段区间放一个点的最小距离和。

显然这个点应该在区间的中位数处,可以暴力做。

复杂度为 \(O(n^3m)\) .

考虑一次转移能不能做到 \(O(1)\) ,能发现 \(w\) 是可以 \(O(n^2)\) 预处理出来的,有

\[w(l,r)=w(l,r-1)+a_r-a_{\lfloor\frac{l+r}{2}\rfloor} \]

但是复杂度仍为 \(O(n^2m)\) .

想一下 \(w\) 是否满足四边形不等式。

由递推式得

\[w(l,r+1)-w(l,r)=a_{r+1}-a_{\lfloor\frac{l+r+1}{2}\rfloor} \]

\[w(l+1,r+1)-w(l+1,r)=a_{r+1}-a_{\lfloor\frac{l+r+2}{2}\rfloor} \]

两式相减有

\[w(l,r+1)+w(l+1,r)-w(l,r)-w(l+1,r+1)=a_{\lfloor\frac{l+r+1}{2}\rfloor}-a_{\lfloor\frac{l+r+2}{2}\rfloor} \]

由于坐标递增得上式 \(\le 0\) .

所以

\[w(l,r)+w(l+1,r+1)\le w(l,r+1)+w(l+1,r) \]

\(f\) 满足四边形不等式,于是可以利用决策单调性进行优化。

时间复杂度 \(O(nm)\) .

#include<bits/stdc++.h>
#define N 3010
#define M 305
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
int n,m,a[N];
int w[N][N],f[N][M],b[N][M];
void init(){
	for(int l=1;l<=n;l++){
		w[l][l]=0;
		for(int r=l+1;r<=n;r++)
			w[l][r]=w[l][r-1]+a[r]-a[(l+r)>>1];
	}
}
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;i++)
		a[i]=read();
	sort(a+1,a+1+n);
	memset(f,0x3f,sizeof(f));
	init(),f[0][0]=0;
	for(int j=1;j<=m;j++){
		b[n+1][j]=n;
		for(int i=n;i;i--){
			for(int k=b[i][j-1];k<=b[i+1][j];k++)
				if(f[i][j]>f[k][j-1]+w[k+1][i])
					f[i][j]=f[k][j-1]+w[k+1][i],b[i][j]=k;
		}	
	}
	printf("%d\n",f[n][m]);
	
	return 0;
}

P3515 [POI2011]Lightning Conductor

序列 \(\{a_n\}\),对于每个 \(i\in\lbrack1,n\rbrack\),求出最小的非负整数 \(p\) 满足 \(\forall j\in\lbrack1,n\rbrack\)\(a_j\le a_i+p-\sqrt{|i-j|}\) .

\(n\le 5\times10^5\)\(0\le a_i\le 10^9\) .

\[p\ge a_j+\sqrt{|i-j|}-a_i \]

\[p=\lceil\max\{a_j+\sqrt{|i-j|}\}\rceil-a_i \]

正着做一次,翻转序列后再做一次就可以消掉绝对值。

\(f(i)=\max\{a_j+\sqrt{i-j}\}\)\(w(j,i)=\sqrt{i-j}\) .

\(l+1<r\)

\[w(l,r+1)+w(l+1,r)-w(l,r)-w(l+1,r+1) \]

\[=\sqrt{r-l-1}+\sqrt{r-l+1}-2\sqrt{r-l} \]

\(t=r-l\)

\[=(\sqrt{t+1}-\sqrt{t})-(\sqrt{t}-\sqrt{t-1}) \]

这个式子是 \(<0\) 的。

那么 \(\forall a\le b\le c\le d\),有 \(w(a,d)+w(b,c)\le w(a,c)+w(b,d)\) .

本题求 \(\max\),将符号取反,可以证明 \(f\) 满足决策单调性。

定义 \(P_i\)\(i\) 的最优决策点。

对于 \(\forall j\in\lbrack0,P_i-1\rbrack\),有

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

因为 \(w\) 满足四边形不等式,对于 \(\forall i'\in\lbrack i+1,n\rbrack\),有

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

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

与第一个不等式相加

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

\(i'\) 的最优决策点一定 \(\ge P_i\)\(f\) 具有决策单调性。

求出 \(f(i)\) 是考虑其可以作为哪些 \(f(i')\) 的最优决策。

根据决策单调性,可以找到一个 \(pos\),这之前的决策都比 \(i\) 优,后面的都比 \(i\) 差。

将该位置及其后面的部分都变为 \(i\),此时它们的最优决策点均为 \(i\) .

建立一个队列代替 \(P\),保存三元组 \((j,l,r)\),表示 \(P(l\)~\(r)\) 都是 \(j\) .

对于每个 \(i\),执行如下操作:

\(1.\) 设队头为 \((j_0,l_0,r_0)\),若 \(r_0=i-1\) 则删除队头,因为 \(f(i)\) 之前的值都已求出,否则令 \(l_0=i\) .

\(2.\) 取队头的最优决策 \(j\) 转移算出 \(f(i)\) .

\(3.\) 插入新决策 \(i\)

\(\enspace (1).\) 取出队尾 \((j_t,l_t,r_t)\) .

\(\enspace (2).\) 若对于 \(f(l_t)\)\(i\)\(j_t\) 更优,即 \(f(i)+w(i,l_t)\le f(j)+w(j_t,l_t)\),记 \(pos=l_t\),删除队尾返回到 \((1)\) .

\(\enspace (3).\) 若对于 \(f(r_t)\)\(i\)\(j_t\) 更优,去往 \((5)\) .

\(\enspace (4).\) 否则,在 \(\lbrack l_t,r_t\rbrack\) 上二分出 \(pos\),在此之前决策优于 \(i\),后面劣于 \(i\),将 \(\lbrack l_t,r_t\rbrack\) 改为 \(\lbrack l_t,pos-1\rbrack\),去往 \((5)\) .

\(\enspace (5).\) 将三元组 \((i,pos,n)\) 插入队尾。

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

这题把符号取反就好。

#include<bits/stdc++.h>
#define db double
#define N 500010
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
int n,a[N],h,t;
db f[N],sqr[N];
struct node{
	int l,r,p;
}q[N];
db w(int j,int i){
	return db(a[j])+sqr[i-j];
}
int find(int t,int x){
	int pos=q[t].r+1,l=q[t].l,r=q[t].r,mid;
	while(l<=r){
		mid=(l+r)>>1;
		if(w(q[t].p,mid)<=w(x,mid))pos=mid,r=mid-1;
		else l=mid+1;
	}
	return pos;
}
void insert(int i){
	q[t].l=max(q[t].l,i);
	while(h<=t&&w(i,q[t].l)>=w(q[t].p,q[t].l))t--;
	if(h>t)q[++t]=(node){i,n,i};
	else{
		int pos=find(t,i);
		if(pos>n)return;
		q[t].r=pos-1;
		q[++t]=(node){pos,n,i};
	}
}
void work(){
	h=1,t=0;
	for(int i=1;i<=n;i++){
		insert(i);
		if(h<=t&&q[h].r<i)h++;
		else q[h].l=i;
		f[i]=max(f[i],w(q[h].p,i));
	}
}
int main(){
	n=read();
	for(int i=1;i<=n;i++){
		a[i]=read();
		sqr[i]=sqrt(i);
	}
	work();
	for(int i=1;i<=n/2;i++)
		swap(a[i],a[n-i+1]),swap(f[i],f[n-i+1]);
	work();
	for(int i=n;i;i--)
		printf("%d\n",(int)ceil(f[i])-a[i]);
	
	return 0;
}

没总结出来比较干练的dp模板。

P1912 [NOI2009] 诗人小G

\(n\) 个字符串,长度分别为 \(a_i\),将它们按顺序划分成若干段,一段区间 \(\lbrack l,r\rbrack\) 的代价是

\[\Big|(a_l+1+a_{l+1}+1+\dots+a_{r-1}+1+a_r)-L\Big|^P \]

试最小化总代价并给出方案。多测。

  • 若答案大于 \(10^{18}\) 输出 Too hard to arrange.

  • 极限数据有 \(T\le5\)\(n\le10^5\)\(L\le3\times10^6\)\(P\le10\).

  • 所有 \(a_i\)\(\le30\).

默认 \(a_i+1\),记 \(s_x=\sum_{i=1}^{x}a_i\).

\(\rm dp\) 方程是

\[f_i=\min_{j=0}^{i-1}\{f_j+|s_i-s_j-(L+1)|^P\} \]

这个东西是高次的所以斜优不了。

若证明 \(w(j,i)=|s_i-s_j-(L+1)|^P\) 满足四边形不等式,就可以用单调队列解决。

\[\Rightarrow w(a,b+1)+w(a+1,b)\ge w(a,b)+w(a+1,b+1) \]

\[u=s_i-s_j-(L+1),v=s_i-s_j-a_j-(L+1) \]

\[|u+a_{i+1}|^P+|v|^P\ge|u|^P+|v+a_{i+1}|^P \]

\[\Rightarrow |v|^P-|v+a_{i+1}|^P\ge|u|^P-|u+a_{i+1}|^P \]

由于 \(u\ge v\),即证 \(g(x)=|x|^P-|x+z|^P(z\in\lbrack0,+\infty))\) 非严格单调递减。

拆一下绝对值。

  • \(x\in\lbrack0,\infty)\):肉眼看比较显然。

求导?

\[g'(x)=Px^{P-1}-P(x+z)^{P-1} \]

容易发现 \(g'(x)<0\).

  • \(x\in(-\infty,0),P\equiv0\space(\text{mod}\space 2)\)

就是 \((-x)^P-(-x+z)^P\),和上面一样是递减的。

  • \(x\in\lbrack-z,0),P\equiv1\space(\text{mod}\space 2)\)

\[g(x)=-x^P-(x+z)^P \]

\[g'(x)=-Px^{P-1}-P(x+z)^{P-1} \]

\(P\) 为奇数所以 \(g'(x)\le 0\).

  • \(x\in(-\infty,-z),P\equiv 1\space(\text{mod}\space 2)\)

\[g(x)=-x^P+(x+z)^P \]

\[g'(x)=-Px^{P-1}+P(x+z)^{P-1} \]

同样 \(g'(x)\le 0\).

不知道为什么要这么严谨证。

把流程再放一遍。还是不太会。

建立一个队列代替 \(P\),保存三元组 \((j,l,r)\),表示 \(P(l\)~\(r)\) 都是 \(j\) .

对于每个 \(i\),执行如下操作:

\(1.\) 设队头为 \((j_0,l_0,r_0)\),若 \(r_0=i-1\) 则删除队头,因为 \(f(i)\) 之前的值都已求出,否则令 \(l_0=i\) .

\(2.\) 取队头的最优决策 \(j\) 转移算出 \(f(i)\) .

\(3.\) 插入新决策 \(i\)

\(\enspace (1).\) 取出队尾 \((j_t,l_t,r_t)\) .

\(\enspace (2).\) 若对于 \(f(l_t)\)\(i\)\(j_t\) 更优,即 \(f(i)+w(i,l_t)\le f(j)+w(j_t,l_t)\),记 \(pos=l_t\),删除队尾返回到 \((1)\) .

\(\enspace (3).\) 若对于 \(f(r_t)\)\(i\)\(j_t\) 更优,去往 \((5)\) .

\(\enspace (4).\) 否则,在 \(\lbrack l_t,r_t\rbrack\) 上二分出 \(pos\),在此之前决策优于 \(i\),后面劣于 \(i\),将 \(\lbrack l_t,r_t\rbrack\) 改为 \(\lbrack l_t,pos-1\rbrack\),去往 \((5)\) .

\(\enspace (5).\) 将三元组 \((i,pos,n)\) 插入队尾。

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

答案大于 \(10^{18}\) 的时候会炸 \(\rm long\space long\),要开 \(\text{long double}\) 牺牲精度换取值域。

贺了一个长得比较像斜优的题解。

#include<bits/stdc++.h>
#define ll long long
#define lb long double
#define N 100010
#define R 32
using namespace std;
char st[N][R];
int T,n;
ll L,P,s[N];lb f[N];
int h,t,q[N],pre[N];
int stk[N],top;
lb qpow(lb k,ll b){
	lb ret=1;
	while(b){
		if(b&1)ret*=k;
		b>>=1,k*=k;
	}
	return ret;
}
lb calc(int j,int i){
	return f[j]+qpow(abs(s[i]-s[j]-L),P);
}
int find(int t,int x){
	if(calc(t,n)<calc(x,n))return n+1;
	int pos=-1,l=x,r=n,mid;
	while(l<=r){
		mid=(l+r)>>1;
		if(calc(x,mid)<=calc(t,mid))pos=mid,r=mid-1;
		else l=mid+1;
	}
	return pos;
}
void work(){
	h=1,t=0,q[++t]=0;
	for(int i=1;i<=n;i++){
		while(h<t&&find(q[h],q[h+1])<=i)h++;
		pre[i]=q[h],f[i]=calc(q[h],i);
		while(h<t&&find(q[t-1],q[t])>=find(q[t],i))t--;
		q[++t]=i;
	}
}
int main(){
	scanf("%d",&T);
	while(T--){
		scanf("%d%lld%lld",&n,&L,&P),L++;
		for(int i=1;i<=n;i++){
			scanf("%s",st[i]);
			s[i]=s[i-1]+strlen(st[i])+1;
		}
		work();
		if(f[n]>1e18){
			printf("Too hard to arrange\n");
		}
		else{
			printf("%lld\n",(ll)f[n]),top=0;
			for(int tp=n;tp;tp=pre[tp])stk[++top]=tp;
			stk[++top]=0;
			for(int i=top;i>1;i--){
				for(int j=stk[i]+1;j<stk[i-1];j++)
					printf("%s ",st[j]);
				printf("%s\n",st[stk[i-1]]);
			}
		}
		printf("--------------------\n");
	}
	
	return 0;
}

Yet Another Minimization Problem

将序列 \(a\) 分成 \(m\) 段,每一段 \(\lbrack l,r\rbrack\) 的代价是其中相同元素的对数,即 \(\sum_{l\le x<y\le r}\lbrack a_x=a_y\rbrack\).

试最小化总代价。

\(2\le n\le 10^5\)\(2\le m\le \min(n,20)\)\(1\le a_i\le n\).

\(f(i,k)\) 为前 \(i\) 个划分成 \(k\) 段的最小代价。

\[f(i,k)=\min_{j=0}^{i-1}\{f(j,k-1)+w(j+1,i)\} \]

证明 \(w\) 满足四边形不等式:

\[w(a,b+1)+w(a+1,b)-w(a,b)-w(a+1,b+1) \]

\[\Rightarrow w(a,b+1)-w(a,b)+w(a+1,b)-w(a+1,b+1) \]

\[\Rightarrow \sum_{p=a}^{b}\lbrack a_p=a_{b+1}\rbrack-\sum_{p=a+1}^{b}\lbrack a_p=a_{b+1}\rbrack \]

\[=\lbrack a_{a}=a_b\rbrack\ge 0 \]

怎么用比较优秀的复杂度求出 \(w\) 函数?可以像莫队一样左右移动指针做。

但是使用单调队列无法很好地保证指针移动的次数。这个时候就有必要用到分治优化了。

\(\text{dp}(l,r,bl,br)\) 表示已经确定 \(f\lbrack l\rbrack\) ~ \(f\lbrack r\rbrack\) 的最优决策点在 \(\lbrack bl,br\rbrack\),找到当前 \(mid\) 的最优决策 \(bm\),然后两边继续递归,也就是 \(\text{dp}(l,mid-1,bl,bm)\)\(\text{dp}(mid+1,r,bm,br)\) .

由于决策单调性它的时间复杂度是 \(O(n\log n)\) 的,不太会证。

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

#include<bits/stdc++.h>
#define ll long long
#define N 100010
#define M 25
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')w=-1;ch=getchar();}
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x*w;
}
int n,m,k,a[N];
int L,R,cnt[N];
ll sum,f[N][M];
int h,t,q[N];
void add(int x){
	sum+=(cnt[x]++);
}
void del(int x){
	sum-=(--cnt[x]);
}
ll w(int l,int r){
	while(L>l)add(a[--L]);
	while(R<r)add(a[++R]);
	while(L<l)del(a[L++]);
	while(R>r)del(a[R--]);
	return sum;
}
void dp(int l,int r,int bl,int br){
	if(l>r)return;
	int mid=(l+r)>>1,bm=bl;
	ll minn=f[bm][k-1]+w(bm+1,mid);
	for(int i=bl;i<=min(br,mid-1);i++){
		if(minn>f[i][k-1]+w(i+1,mid))
			bm=i,minn=f[i][k-1]+w(i+1,mid);
	}
	f[mid][k]=minn;
	dp(l,mid-1,bl,bm),dp(mid+1,r,bm,br);
}
int main(){
	n=read(),m=read();
	L=1,R=n;
	for(int i=1;i<=n;i++){
		a[i]=read(),sum+=(cnt[a[i]]++);
		f[i][1]=sum;
	}
	for(k=2;k<=m;k++)dp(1,n,1,n);
	printf("%lld\n",f[n][m]); 
	
	return 0;
}