luogu P1912 [NOI2009] 诗人小G
题面传送门
这个东西显然有\(O(n^2)\)的\(dp\)式子\(f_i=\min\limits_{j=1}^{i-1}{dp_j+|\sum\limits_{k=j+1}^{i}{len_k}+i-j-1-L|^p}\)
但是这个东西没有办法斜率优化或者数据结构优化。
考虑决策单调性优化。
如何证明其有决策单调性?显然这个东西满足四边形不等式,感性理解一下,如果转移点往左移,对于小于\(L\)是矛盾的,对于大于\(L\)是不会更优的。
所以可以决策单调性的套路二分+单调队列。
具体一点,就是如果队尾的值在现在转移就比现在的状态劣,那么可以弹出。如果左边更优右边更劣那就二分出边界点。
时间复杂度\(O(T(nlogn+n|S|))\)
注意要用long double,long long撑不下。
code:
#include<cstdio>
#include<cstring>
#define abs(x) ((x)>0?(x):-(x))
#define lb long double
#define ull unsigned long long
using namespace std;
int n,m,k,x,y,z,T,last[100039],a[100039],mid,now,ah,head,tail,l,r,L,p,len[100039];
lb dp[100039];
char s[100039][39];
struct que{int c,l,r;}q[100039];
inline lb mpow(lb x,int y){lb ans=1;while(y)(y&1)?(ans=ans*x):0,x=x*x,y>>=1;return ans;}
#define calc(x,y) (dp[x]+mpow(abs(len[y]-len[x]+y-x-1-L),p))
inline void solve(){
register int i,j;a[ah=1]=n;now=n;
while(now) now=last[now],a[++ah]=now;
for(i=ah-1;i;i--){
for(j=a[i+1]+1;j<a[i];j++)printf("%s ",s[j]+1);
printf("%s\n",s[a[i]]+1);
}
}
int main(){
freopen("1.in","r",stdin);
register int i;
scanf("%d",&T);
while(T--){
scanf("%d%d%d",&n,&L,&p);memset(dp,0,sizeof(dp));memset(last,0,sizeof(last));q[head=tail=0]=(que){0,1,n};
for(i=1;i<=n;i++) scanf("%s",s[i]+1),len[i]=strlen(s[i]+1),len[i]+=len[i-1];
for(i=1;i<=n;i++){
while(head<=tail&&q[head].r<i) head++;dp[i]=calc(q[head].c,i);last[i]=q[head].c;
while(head<=tail&&(q[tail].l<i?(q[tail].l=i,1):1)&&calc(q[tail].c,q[tail].l)>=calc(i,q[tail].l)) tail--;
if(head>tail){q[++tail]=(que){i,i,n};continue;}l=q[tail].l-1;r=q[tail].r+1;
while(l+1<r)
mid=l+r>>1,calc(i,mid)<calc(q[tail].c,mid)?r=mid:l=mid;
r==q[tail].l?tail--:q[tail].r=l;if(r<=n)q[++tail]=(que){i,r,n};
}
if(dp[n]>1e18) printf("Too hard to arrange\n");
else printf("%lld\n",(ull)dp[n]),solve();
printf("--------------------\n");
}
}