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;
}
本文来自博客园,作者:myee,转载请注明原文链接:https://www.cnblogs.com/myee/p/Luogu-solution-p1912.html