2 条题解
-
2
白日依山尽,黄河入海流。
欲穷千里目,更上一层楼。
大家好,这里是 myee,一个不会决策单调性的小萌新!
设第 句话长度为 ,定义 。
则显然把第 行写在一起的代价为 。(考虑空格)
注意到 满足四边形恒等式与区间包含单调性, 有凸性。
因此 满足四边形不等式,进而对 1D1D dp
有决策单调性。而 正是我们所要求的。
啥,为什么 有凸性?
只用证 有凸性。
由对称性,只用证 时 有凸性。
注意到这个东西二阶导在 时总是非负。
因此其(非严格)下凸得证。
接下来就是考虑如何利用这个东西优化 dp 了。
由于最小最优决策点单调不减,且函数单调增,考虑单调队列。
维护每个决策点的对应最右目标,即其贡献区间。
查询时如果队头元素到决策点最优目标了,记得出队。然后用队头元素更新当前解。
更新完后,二分当前决策点的贡献区间,与当前队尾决策点相比较来一决胜负,从而更新队尾与当前决策点的贡献区间。
如果队尾的贡献区间缩没了,将其出队。
于是这题就做完辣!!!
但是这个输出方案还是未免太困难了点吧。qwq
下面是代码。
注意坑点:
- 开
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; }
- 开
-
0
#include<cstdio> #include<cmath> #include<cstring> #define RG register #define R RG int #define G c=getchar() #define Calc(i,j) f[j]+qpow(abs(s[i]-s[j]-L))//计算函数值 using namespace std; typedef long double LD;//开long double const int N=1e5+9; int n,L,P,s[N],q[N],k[N],pr[N]; LD f[N]; char str[N][33]; inline int in(){ RG char G; while(c<'-')G; R x=c&15;G; while(c>'-')x*=10,x+=c&15,G; return x; } inline LD qpow(RG LD b){//自己写快速幂 RG LD a=1; for(R k=P;k;k>>=1,b*=b) if(k&1)a*=b; return a; } inline int bound(R x,R y){//二分临界值 R 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; } int main(){ R T=in(),i,h,t; while(T--){ n=in();L=in()+1;P=in();//把L处理了一下 for(i=1;i<=n;++i){ if(scanf("%s",str[i])); s[i]=s[i-1]+strlen(str[i])+1;//记前缀和 } for(q[i=h=t=1]=0;i<=n;++i){ while(h<t&&k[h]<=i)++h; f[i]=Calc(i,q[h]);pr[i]=q[h];//记录转移位置方便输出方案 while(h<t&&k[t-1]>=bound(q[t],i))--t; k[t]=bound(q[t],i);q[++t]=i; } if(f[n]>1e18)puts("Too hard to arrange"); else{ printf("%.0Lf\n",f[n]); for(q[t=0]=i=n;i;q[++t]=i=pr[i]); for(;t;--t){ for(i=q[t]+1;i<q[t-1];++i) printf("%s ",str[i]); puts(str[i]);//行末不要搞空格 } } puts("--------------------"); } return 0; }
- 1
信息
- ID
- 1563
- 时间
- 2000ms
- 内存
- 256MiB
- 难度
- 6
- 标签
- (无)
- 递交数
- 58
- 已通过
- 17
- 上传者