诗人小G 四边形不等式
诗人小G 四边形不等式优化
1 状态设计与转移
不难写出 dp 状态设计与方程:设 \(f_{i}\) 表示把前 \(i\) 个句子合并为后的最优值是多少,有:
2 决策单调性分析
明显 \(n^2\) ,不过题目要求我们 \(O(n\log n)\) ,后面这个 \(P\) 次方明显不能斜率优化与单调队列优化,更没有什么好的数据结构可以维护。我们考虑上面的方程是否具有决策单调性。
我们画出 \(f(x)=|x|^P\) 的图像,大约长这样:
这是指数分别为 \(1\) 到 \(4\) 的时候。我们发现所有的函数的形状与二次函数类似,所以我们在做题的时候就可以在二次函数上研究。
令 \(w(k,i)=|sum_i-sum_k+i-k+1-L|^P\),我们可以写出:
观察到 dp 方程时取 \(\min\) ,所以我们要证明的是:
首先先观察 \(w\) 函数有什么性质,发现如果把每个绝对值里面的数当成横坐标,画到函数上,有一些可以利用的规律。
首先,令上面四个函数的横坐标分别为 \(w_1,w_2,w_3,w_4\) ,然后把需要证明的式子移项:
分别对应横坐标为 \(w_4,w_2,w_3,w_1\) 。
不难发现,\(w_4\) 与 \(w_2\) 相差 \(sum_{i+1}+1\) ,\(w_3,w_1\) 相差 \(sum_{i+1}+1\) ,所以我们考虑利用幂函数的增速递增来证明这个事情。
不难发现,横坐标满足:
证明上面这个式子只需注意到 \(sum\) 递增,所以 \(sum_{i+1}>sum_{k+1}\) 。
当 \(w_3\) 对应点的位置大于 \(w_2\) 时,如图:
式子明显是成立的。
当 \(w_3\) 的对应点大于 \(w_2\) 的时候,如图:
能看到虽然 \(w_2\) 的绝对值很大,但是因为是做减法,所以式子任然是成立的。
证明虽然不是很严谨,但能直观的感受到,这个式子确实是满足四边形不等式,那么根据 dp 决策单调性的经典结论,\(f\) 满足决策单调性。
3 代码:
#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 1001000
#define M number
using namespace std;
const int INF=0x3f3f3f3f;
template<typename T> inline void read(T &x) {
x=0; int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
for(;isdigit(c);c=getchar()) x=x*10+c-'0';
x*=f;
}
inline bool check_val(ll val){
if(val>1e18||val<0) return 0;
return 1;
}
inline ld ksm(ld a,int b){
ld res=1;
while(b){if(b&1) res*=a;a=a*a;b>>=1;}
return res;
}
char s[N][32];
ll t,n,L,P,g[N];
ld f[N],len[N],sum[N];
struct node{
ll k,l,r;
inline node(){}
inline node(ll k,ll l,ll r) : k(k),l(l),r(r) {}
}q[N];
int head,tail;
inline ld calc(int k,int i){
return f[k]+ksm(abs(sum[i]-sum[k]+i-k-1-L),P);
}
inline ll erfen(ll k,ll l,ll r){
while(l<r){
ll mid=(l+r)>>1;
if(calc(q[tail].k,mid)<calc(k,mid)) l=mid+1;
else r=mid;
}
return l;
}
inline void insert(ll k){
if(head==tail){q[++tail]=node(k,1,n);return;};ll pos;
while(head<tail&&q[tail].l>=k+1&&calc(q[tail].k,q[tail].l)>calc(k,q[tail].l)) tail--;
if(q[tail].r>=k+1&&calc(q[tail].k,q[tail].r)<calc(k,q[tail].r)){
if(q[tail].r!=n) pos=q[tail].r+1;else return;
}
else pos=erfen(k,max(q[tail].l,k+1),q[tail].r);
q[tail].r=pos-1;q[++tail]=node(k,pos,n);
}
inline void clear(){
memset(len,0,sizeof(len));memset(f,0,sizeof(f));
}
inline void print(int k){
if(g[k]!=0) print(g[k]);
for(int i=g[k]+1;i<=k;i++){
printf("%s",s[i]);
if(i!=k) putchar(' ');
}
printf("\n");
}
int main(){
// freopen("my.in","r",stdin);
// freopen("my.out","w",stdout);
read(t);
while(t--){
// printf("%lld\n",t);
clear();
read(n);read(L);read(P);
// printf("here\n");
for(int i=1;i<=n;i++){
scanf("%s",s[i]);len[i]=strlen(s[i]);sum[i]=sum[i-1]+len[i];
// printf("i:%d len:%lld\n",i,len[i]);//alright
}
// printf("here\n");
head=tail=0;insert(0);
for(int i=1;i<=n;i++){
while(head<tail&&q[head+1].r<=i-1) head++;
if(head<tail){
int k=q[head+1].k;
f[i]=calc(k,i);g[i]=k;
// printf("zhuanyi:i:%d k:%d\n",i,k);
}
insert(i);
}
if(f[n]>1e18) printf("Too hard to arrange\n");
else{
printf("%lld\n",(ll)f[n]);
print(n);
}
printf("--------------------");
if(t!=0) printf("\n");
}
return 0;
}