Loading

诗人小G 四边形不等式

诗人小G 四边形不等式优化

1 状态设计与转移

不难写出 dp 状态设计与方程:设 \(f_{i}\) 表示把前 \(i\) 个句子合并为后的最优值是多少,有:

\[f_{i}=\min\limits_{0\le k<i}\{f_k+|sum_i-sum_k+i-k+1-L|^P \} \]

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\),我们可以写出:

\[\begin{align} w(k,i)=&|sum_i-sum_k+i-k+1-L|^P\tag1\\ w(k+1,i)=&|sum_i-sum_{k+1}+i-k-L|^P \tag2\\ w(k,i+1)=&|sum_{i+1}-sum_k+i-k+2-L|^P\tag3\\ w(k+1,i+1)=&|sum_{i+1}-sum_{k+1}+i-k+1-L|^P\tag4 \end{align} \]

观察到 dp 方程时取 \(\min\) ,所以我们要证明的是:

\[w(k,i)+w(k+1,i+1)\le w(k,i+1)+w(k+1,i) \]

首先先观察 \(w\) 函数有什么性质,发现如果把每个绝对值里面的数当成横坐标,画到函数上,有一些可以利用的规律。

首先,令上面四个函数的横坐标分别为 \(w_1,w_2,w_3,w_4\) ,然后把需要证明的式子移项:

\[w(k+1,i+1)-w(k+1,i)\le w(k,i+1)-w(k,i)\tag5 \]

分别对应横坐标为 \(w_4,w_2,w_3,w_1\)

不难发现,\(w_4\)\(w_2\) 相差 \(sum_{i+1}+1\)\(w_3,w_1\) 相差 \(sum_{i+1}+1\) ,所以我们考虑利用幂函数的增速递增来证明这个事情。

不难发现,横坐标满足:

\[w_2<w_1<w_4<w_3 \]

证明上面这个式子只需注意到 \(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;
}
posted @ 2021-07-04 17:05  hyl天梦  阅读(72)  评论(0编辑  收藏  举报