[BZOJ1563][NOI2009]诗人小G(dp+决策单调性)

245 篇文章 0 订阅
4 篇文章 0 订阅

题目描述

传送门

题解

数据太大了中间要用long double

显然每一个句子只有长度是有价值的
f(i) 表示前i个句子放好的最小不协调度
裸dp O(n2)
还是需要优化

记录一下决策发现有单调性
但是这道题 f(i) 需要从 f(j) 转移过来
转一个图 地址:http://www.bubuko.com/infodetail-225479.html
这里写图片描述
加一个双向链表或者数据结构也行?

代码

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
using namespace std;
#define LL long long
#define N 100005

const long double inf=1e18;
int T,n,P,pos[N],pre[N],nxt[N];
char word[50];
long double L,a[N],s[N],f[N];

long double fast_pow(long double a,int p)
{
    long double ans=1.0;
    if (a<0) a=-a;
    for (;p;p>>=1,a*=a)
        if (p&1)
            ans*=a;
    return ans;
}
long double calc(int i,int j)
{
    return f[j]+fast_pow(s[i]-s[j]+(long double)i-(long double)j-1.0-L,P);
}
bool check(int mid,int x,int y)
{
    long double p=calc(mid,x);
    long double q=calc(mid,y);
    return p>=q;
}
int find(int l,int r,int x,int y)
{
    int mid,ans=n+1;
    while (l<=r)
    {
        mid=(l+r)>>1;
        if (check(mid,x,y)) ans=mid,r=mid-1;
        else l=mid+1;
    }
    return ans;
}
int main()
{
    scanf("%d",&T);
    while (T--)
    {
        cin>>n>>L>>P;
        for (int i=1;i<=n;++i)
        {
            scanf("%s",word);
            a[i]=(long double)strlen(word);s[i]=s[i-1]+a[i];
        }
        for (int i=0;i<=n;++i) f[i]=inf+1,pos[i]=n+1;
        for (int i=0;i<=n;++i) pre[i]=i-1,nxt[i]=i+1;
        f[0]=0;pos[0]=1;
        int now=0;
        for (int i=1;i<=n;++i)
        {
            while (pos[nxt[now]]<=i)
                now=nxt[now];
            long double p=calc(i,now);
            f[i]=min(f[i],p);
            while (pos[pre[i]]>i)
            {
                if (check(pos[pre[i]],pre[i],i))
                {
                    pre[i]=pre[pre[i]];
                    nxt[pre[i]]=i;
                }
                else break;
            }
            pos[i]=find(pos[pre[i]],n,pre[i],i);
            if (pos[i]>n) nxt[pre[i]]=nxt[i],pre[nxt[i]]=pre[i];
        }
        if (f[n]>inf) puts("Too hard to arrange");
        else cout<<(long long)f[n]<<endl;
        puts("--------------------");
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值