BZOJ 1563: [NOI2009]诗人小G (决策单调性,单调队列,二分)
这算是决策单调性入门题吧.
我们很容易发现 $f[i]$ 的转移 $p_{i}$ 满足单调性,然后拿单调队列来维护就行.
- 对于队列中每个元素维护这个元素转移区间的右端点
- 新加入一个点的时候和队尾比较一下,看队尾是否会被覆盖,弹掉无用元素.
code:
#include <bits/stdc++.h> #define ll long long #define N 100009 #define setIO(s) freopen(s".in","r",stdin) using namespace std; int n,L,P,s[N],q[N],k[N],pr[N]; long double f[N]; char str[N][33]; long double qpow(long double x) { long double tmp=1; for(int k=P;k;k>>=1,x*=x) if(k&1) tmp*=x; return tmp; } long double calc(int i,int j) { return f[j]+qpow(abs(s[i]-s[j]-L)); } int find(int x,int y) { if(calc(n,x)<=calc(n,y)) return n+1; int l=y,r=n+1,mid,ans=0; while(l<=r) { mid=(l+r)>>1; if(calc(mid,x)>=calc(mid,y)) ans=mid,r=mid-1; else l=mid+1; } return ans; } void solve() { scanf("%d%d%d",&n,&L,&P),++L; for(int i=1;i<=n;++i) { if(scanf("%s",str[i])); s[i]=s[i-1]+strlen(str[i])+1; } int h=1,t=1; q[h]=0; for(int i=1;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]>=find(q[t],i)) --t; k[t]=find(q[t],i),q[++t]=i,k[t]=n+1; } if(f[n]>1e18) printf("Too hard to arrange\n"); else { printf("%.0Lf\n",f[n]); } puts("--------------------"); } int main() { // setIO("input"); int T; scanf("%d",&T); while(T--) solve(); return 0; }