关于决策单调性

Algha_Porthos

2020-01-20 10:41:25

Personal

昨天在Atalod&&网管的帮助下,AC了斜优的板子。那么,现在我要学一学决策单调性。 [P1912](https://www.luogu.com.cn/problem/P1912),[NOI2009]诗人小G。是一道毒瘤DP。 题意:一首诗,有$n$话。你需要排版。可以把若干句连续的话放在同一行,句间空格隔开,**句末不留空格**。有一个标准长度$l$,以及一个乘方$p$。每一行的贡献是$((\sum_{i=l}^r ss[i].size())+r-l+1)^p$。需要让贡献之和最小。求最小的贡献以及按照最小贡献的排版方式排的任意一种情况。多组数据。$n<=1e5$。 这道题我的$n^2 DP$居然只有10分???? 开始决策单调性学习! 对于这一道题,我们显然有$f[i]=min_{0}^{i-1}\ \ f[j]+(abs(l-(s[i]-s[j]-1)))^p$的方程。 ``` #include<bits/stdc++.h> #define int long long using namespace std; int T,n,l,p; char str[105005][33]; int s[105005]; long double f[105005]; int pr[105005]; long double pw(long double a,long double b){ long double res=1.0; for(int i=1;i<=b;++i) res*=a; return res; } void dfs(int u){ if(u==0) return; dfs(pr[u]); for(int i=pr[u]+1;i<u;++i) cout<<str[i]<<' '; cout<<str[u]<<endl; } int bound(int x,int y){//二分临界值 int l=x,r=n+1,m;//左端点设为x减小常数 while(l<r){ m=(l+r)>>1; Calc(m,x)>=Calc(m,y)?r=m:l=m+1; } return l; } long double Calc(int i,int j){ return f[j]+pw(abs(s[i]-s[j]-l-1),p); } signed main(){ cin>>T; for(int _=1;_<=T;++_){ cin>>n>>l>>p; for(int i=1;i<=n;++i){ scanf("%s",str[i]); s[i]=s[i-1]+strlen(str[i])+1; } int be=1,ed=1; for(int i=1;i<=n;++i){ int q[105005],k[105005];///////////////// q[1]=0; while(be<ed&&k[be]<=i) ++be; f[i]=Calc(i,q[be]); pr[i]=q[be];//记录转移位置方便输出方案 while(be<ed&&k[ed-1]>=bound(q[ed],i)) --ed; k[ed]=bound(q[ed],i); q[++ed]=i; } if(f[n]>1e18) puts("Too hard to arrange"); else{ printf("%.0Lf\n",f[n]); dfs(n); } puts("--------------------"); } } ```