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;
}

  

posted @ 2020-04-02 23:06  EM-LGH  阅读(190)  评论(0编辑  收藏  举报