题目大意:
小G是一个出色的诗人,经常作诗自娱自乐。但是,他一直被一件事情所困扰,那就是诗的排版问题。
一首诗包含了若干个句子,对于一些连续的短句,可以将它们用空格隔开并放在一行中,注意一行中可以放的句子数目是没有限制的。小G给每首诗定义了一个行标准长度(行的长度为一行中符号的总个数),他希望排版后每行的长度都和行标准长度相差不远。显然排版时,不应改变原有的句子顺序,并且小G不允许把一个句子分在两行或者更多的行内。在满足上面两个条件的情况下,小G对于排版中的每行定义了一个不协调度, 为这行的实际长度与行标准长度差值绝对值的P次方,而一个排版的不协调度为所有行不协调度的总和。
小G最近又作了几首诗,现在请你对这首诗进行排版,使得排版后的诗尽量协调(即不协调度尽量小),并把排版的结果告诉他。
思路:
首先可以做一个简单的DP,即dp[i]表示写完第i句之后的最小代价,于是有dp[i]=min(dp[j]+|sum[i]-sum[j]+i-j-1-L|^k)。
这样朴素的转移是n^2的,于是可以去优化复杂度,发现dp方程可以写成这个形式:dp[i]=min(dp[j]+w[j][i]),也就是1D1D的形式:如果w[j][i]满足四边形不等式的话,那么这个DP满足决策单调性。
可以打表发现 w[j][i]满足四边形不等式,于是我们要用决策单调性来优化时间。在决策单调性的条件下,决策表只有可能长成这个样子:
111111122222222222233333333333333333333333
不难发现dp[i]在不断更新的时候,i作为决策会取代一段后缀,于是我们只需要二分后缀的起点就好了。
具体的话用一个队列去维护每一个决策点的决策区间,然后在队列中二分。
注意中间结果可能会超出1e18,用long double当作long long,因为它有科学计数法。
// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a,i##_end_=b;i<=i##_end_;++i)
#define DREP(i,a,b) for(int i=a,i##_end_=b;i>=i##_end_;--i)
typedef long double ll;
using namespace std;
void File(){
freopen("bzoj1563.in","r",stdin);
freopen("bzoj1563.out","w",stdout);
}
const int maxn=1e5+10;
const long long inf=LLONG_MAX>>1;
int T,n,L,P,len[maxn],sum[maxn],pre[maxn];
char poet[maxn][35];
ll dp[maxn];
struct node{int l,r,p;}qu[maxn];
int head,tail,ans[maxn],tp;
ll qpow(ll x,int y){
ll ret=1;
while(y){
if(y&1)ret=ret*x;
x=x*x;
y>>=1;
}
return ret;
}
ll cal(int i,int p){return dp[p]+qpow(abs(sum[i]-sum[p]+i-p-1-L),P);}
#define mid ((l+r)>>1)
int find(int id,int np){
int l=qu[id].l,r=qu[id].r,p=qu[id].p;
while(l<r){
if(cal(mid,np)<cal(mid,p))r=mid;
else l=mid+1;
}
return l;
}
void init(){
scanf("%d%d%d",&n,&L,&P);
REP(i,1,n){
scanf("%s",poet[i]+1);
len[i]=strlen(poet[i]+1);
sum[i]=sum[i-1]+len[i];
}
}
void print(){
int x=n,l,r;
while(1){
ans[++tp]=x;
if(!x)break;
x=pre[x];
}
DREP(i,tp,1){
l=ans[i]+1,r=ans[i-1];
REP(j,l,r)printf("%s%c",poet[j]+1,j==r ? '\n' : ' ');
}
}
void work(){
head=tail=1;
qu[tail]=(node){1,n,0};
REP(i,1,n){
while(qu[head].r<i)++head;
dp[i]=cal(i,qu[head].p);
pre[i]=qu[head].p;
int l=head,r=tail;
while(l<r){
if(cal(qu[mid].r,i)<cal(qu[mid].r,qu[mid].p))r=mid;
else l=mid+1;
}
if(cal(qu[l].r,i)>=cal(qu[l].r,qu[l].p))continue;
int pos=find(l,i);
while(qu[tail].l>=pos)--tail;
if(qu[tail].r>=pos)qu[tail].r=pos-1;
qu[++tail]=(node){pos,n,i};
}
if(dp[n]>1e18)puts("Too hard to arrange");
else{
printf("%.0Lf\n",dp[n]);
print();
}
}
int main(){
//File();
scanf("%d",&T);
while(T--){
init();
work();
puts("--------------------");
tp=head=tail=0;
}
return 0;
}