LG P1912 [NOI2009]诗人小G

Description

小 G 是一个出色的诗人,经常作诗自娱自乐。但是,他一直被一件事情所困扰,那就是诗的排版问题。

一首诗包含了若干个句子,对于一些连续的短句,可以将它们用空格隔开并放在一行中,注意一行中可以放的句子数目是没有限制的。小 G 给每首诗定义了一个行标准长度(行的长度为一行中符号的总个数),他希望排版后每行的长度都和行标准长度相差不远。显然排版时,不应改变原有的句子顺序,并且小 G 不允许把一个句子分在两行或者更多的行内。在满足上面两个条件的情况下,小 G 对于排版中的每行定义了一个不协调度, 为这行的实际长度与行标准长度差值绝对值的 $P$ 次方,而一个排版的不协调度为所有行不协调度的总和。

小 G 最近又作了几首诗,现在请你对这首诗进行排版,使得排版后的诗尽量协调(即不协调度尽量小),并把排版的结果告诉他。

Solution

对于前$i$个诗句,选择的上一个换行点具有决策单调性($s$为诗句长度前缀和)

$$dp_i=min_{j < i}(dp_j+|s_i-s_j-l+1|^p)$$

单调队列维护递增的最优转移点

#pragma GCC optimize(2)
#include<iostream>
#include<cstring>
#include<cstdio>
#include<deque>
#include<cmath>
using namespace std;
int T,n,l,p,s[100005],g[100005],r[100005],tot;
char c[100005][35];
long double dp[100005];
deque<int>qpos,q;
inline int read()
{
    int w=0,f=1;
    char ch=0;
    while(ch<'0'||ch>'9')
    {
        if(ch=='-')
            f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9')
    {
        w=(w<<1)+(w<<3)+ch-'0';
        ch=getchar();
    }
    return w*f;
}
long double ksm(long double a,int P)
{
    long double ret=1;
    while(P)
    {
        if(P&1)
        {
            ret*=a;
        }
        a*=a;
        P>>=1;
    }
    return ret;
}
long double cal(int i,int j)
{
    return dp[j]+ksm((long double)abs(s[i]-s[j]-l-1),p);
}
int find(int x,int y)
{
    int l=x,r=n+1,ans=r;
    while(l<=r)
    {
        int mid=l+r>>1;
        if(cal(mid,x)<cal(mid,y))
        {
            l=mid+1;
        }
        else
        {
            ans=mid;
            r=mid-1;
        }
    }
    return ans;
}
int main()
{
    T=read();
    for(;T;T--)
    {
        n=read();
        l=read();
        p=read();
        for(int i=1;i<=n;i++)
        {
            scanf("%s",c[i]);
            s[i]=s[i-1]+strlen(c[i])+1;
        }
        q.clear();
        qpos.clear();
        q.push_back(0);
        qpos.push_back(0);
        for(int i=1;i<=n;i++)
        {
            while(q.size()>1&&qpos[1]<=i)
            {
                q.pop_front();
                qpos.pop_front();
            }
            g[i]=q.front();
            dp[i]=cal(i,g[i]);
            while(q.size()>1&&qpos.back()>=find(q.back(),i))
            {
                qpos.pop_back();
                q.pop_back();
            }
            qpos.push_back(find(q.back(),i));
            q.push_back(i);
        }
        if(dp[n]>1e18)
        {
            puts("Too hard to arrange");
        }
        else
        {
            printf("%lld
",(long long)dp[n]);
            /*for(int i=n;i;i=g[i])
            {
                r[++tot]=g[i];
            }
            r[0]=n;
            while(tot)
            {
                for(int i=r[tot]+1;i<r[tot-1];i++)
                {
                    printf("%s ",c[i]);
                }
                puts(c[r[tot-1]]);
                --tot;
            }*/
        }
        puts("--------------------");
    }
    return 0;
}
诗人小G