Luogu1912

白日依山尽,黄河入海流。
欲穷千里目,更上一层楼。

大家好,这里是 myee,一个不会决策单调性的小萌新!


设第 \(k\) 句话长度为 \(l_k-1\),定义 \(S_k=\sum_{i=1}^kl_k\)

则显然把第 \(l\sim r\) 行写在一起的代价为 \(|S_r-S_{l-1}-L-1|^P\)。(考虑空格)

注意到 \(S_r-S_{l-1}\) 满足四边形恒等式与区间包含单调性,\(|x-L-1|^P\) 有凸性。

因此 \(w(l,r)=|S_r-S_{l-1}-L-1|^P\) 满足四边形不等式,进而对 1D1D dp

\[f_r=\min\{f_{l-1}+w(l,r)|1\le l\le r\} \]

决策单调性。而 \(f_n\) 正是我们所要求的。

啥,为什么 \(|x-L-1|^P\) 有凸性?

只用证 \(|x|^P\) 有凸性。
由对称性,只用证 \(x\ge0\)\(x^P\) 有凸性。
注意到这个东西二阶导在 \(x>0\) 时总是非负。
因此其(非严格)下凸得证。


接下来就是考虑如何利用这个东西优化 dp 了。

由于最小最优决策点单调不减,且函数单调增,考虑单调队列。

维护每个决策点的对应最右目标,即其贡献区间。

查询时如果队头元素到决策点最优目标了,记得出队。然后用队头元素更新当前解。

更新完后,二分当前决策点的贡献区间,与当前队尾决策点相比较来一决胜负,从而更新队尾与当前决策点的贡献区间。

如果队尾的贡献区间缩没了,将其出队。

于是这题就做完辣!!!

但是这个输出方案还是未免太困难了点吧。qwq


下面是代码。

注意坑点:

  • 因为 spj 缺失,现在 Hydro 上必须得删除输出方案的部分才可以通过此题。
  • long double 来保证转移确定决策点时不 gg。
// 由于带绝对值,所以凸性显然。
// 因此可以决策单调性了。
// 太逊了,并不会 SMAWK 算法,直接单调队列二分法了。

// 是他是他就是他。
// 决策单调性优化 dp。
// 咕了很久。
// 这个输出方案还是太离谱了。

#include <algorithm>
#include <deque>
#include <stdio.h>
#include <vector>
typedef long long llt;
typedef unsigned uint;typedef unsigned long long ullt;
typedef bool bol;typedef char chr;typedef void voi;
typedef double dbl;
template<typename T>bol _max(T&a,T b){return(a<b)?a=b,true:false;}
template<typename T>bol _min(T&a,T b){return(b<a)?a=b,true:false;}
template<typename T>T lowbit(T n){return n&-n;}
template<typename T>T gcd(T a,T b){return b?gcd(b,a%b):a;}
template<typename T>T lcm(T a,T b){return(a!=0||b!=0)?a/gcd(a,b)*b:(T)0;}
template<typename T>T exgcd(T a,T b,T&x,T&y){if(b!=0){T ans=exgcd(b,a%b,y,x);y-=a/b*x;return ans;}else return y=0,x=1,a;}
template<typename T>T power(T base,T index,T mod)
{
    T ans=1%mod;
    while(index)
    {
        if(index&1)ans=ans*base%mod;
        base=base*base%mod,index>>=1;
    }
    return ans;
}
typedef long double ldbl;
const ldbl inf=1e18+.000000000000000005;
ldbl Pow(ldbl base,uint index)
{
    ldbl ans=1;
    while(index)
    {
        if(index&1)ans=ans*base;
        base=base*base,index>>=1;
    }
    return ans;
}
uint Right[100005],Left[100005];
chr C[100005][35];
uint Len[100005],S[100005];
ldbl Ans[100005];
int main()
{
#ifdef MYEE
    freopen("QAQ.in","r",stdin);
#endif
    uint t;scanf("%u",&t);
    while(t--)
    {
        uint n,L,p;scanf("%u%u%u",&n,&L,&p);
        for(uint i=1;i<=n;i++)
        {
            scanf("%s",C[i]);Len[i]=1;while(C[i][Len[i]-1])Len[i]++;
            S[i]=S[i-1]+Len[i];
        }
        auto w=[&](uint l,uint r){return S[r]-S[l]>L+1?Pow((S[r]-S[l])-(L+1),p):Pow((L+1)-(S[r]-S[l]),p);};
        Right[0]=n;
        std::deque<uint>D{0};
        for(uint i=1;i<=n;i++)
        {
            while(Right[D.front()]<i)D.pop_front();
            Left[i]=D.front();Ans[i]=Ans[D.front()]+w(D.front(),i);
            if(Ans[i]>inf)continue;
            while(true)
            {
                uint p=D.back();
                uint l=i,r=n;
                while(l<r)
                {
                    uint mid=(l+r+1)>>1;
                    if(Ans[p]+w(p,mid)<=Ans[i]+w(i,mid))l=mid;
                    else r=mid-1;
                }
                Right[p]=l;
                if(D.size()>1&&Right[D[D.size()-2]]>=l)D.pop_back();
                else break;
            }
            Right[i]=n;D.push_back(i);
        }
        if(Ans[n]>inf)puts("Too hard to arrange");
        else{
            printf("%lld\n",(llt)Ans[n]);
            std::vector<uint>P;
            uint p=n;while(p)P.push_back(p),p=Left[p];
            std::reverse(P.begin(),P.end());
            for(auto v:P)while(p<v)p++,printf("%s%c",C[p]," \n"[p==v]);
        }
        puts("--------------------");
    }
    return 0;
}
posted @ 2022-09-13 16:47  myee  阅读(27)  评论(0编辑  收藏  举报