2019多校第三场 HDU6606 Distribution of books(二分,权值线段树维护,DP)

链接:HDU6606 Distribution of books

题意:

将一个长度为n的序列a[1]、a[2]、… 、a[n], 要求取前k段(要求各段连续,但不可交叉,每段元素个数≥1),使得最大的那段和最小,并输出该和。

1 <= n <= 2*105
1 <= k <= n
-109 <= ai <= 109



分析:

先求下前缀 s u m sum sum,再二分得到答案,对于可能的答案x,利用dp进行验证。

d p [ i ] : dp[i]: dp[i]:表示 i i i个数 可以得到的 最多 符合条件段数

d p [ i ] = m a x { d p [ j ] } + 1 &ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace;&ThickSpace; ( 0 ≤ j &lt; i , s u m [ i ] − s u m [ j ] ≤ x ) dp[i]=max\{dp[j]\}+1\;\;\;\;\;(0\le j&lt;i,sum[i]-sum[j]\le x) dp[i]=max{dp[j]}+1(0j<i,sum[i]sum[j]x)
若最后存在 d p [ i ] ≥ k dp[i]\ge k dp[i]k,说明 x x x是可能的答案,否则不可能是答案。

x x x满足该条件,那 &gt; x \gt x >x的数肯定满足该条件,答案只有可能 ≤ x \le x x,则令 R = M I D − 1 R=MID-1 R=MID1,但要注意,最后一次二分得到的 M I D MID MID未必是答案,因为其小于上一个 x x x,但未必可行,所以答案应是 最后一次满足条件的 x x x

x x x不满足该条件,答案肯定 &gt; x \gt x >x,则令 L = M I D + 1 L=MID+1 L=MID+1


注意: 因为要求的各分段之间是连续的,所以 d p dp dp的初值不能设为0,而应当 全设为 − ∞ -\infty ,然后令 d p [ 0 ] = 0 dp[0]=0 dp[0]=0,这样就可以保证连续性。


对于查找 m a x { d p [ j } max\{dp[j\} max{dp[j},可 将sum离散化后权值线段树维护

s u m [ i ] − s u m [ j ] ≤ x sum[i]-sum[j]\le x sum[i]sum[j]x可转化为 s u m [ j ] ≥ s u m [ i ] − x sum[j]\ge sum[i]-x sum[j]sum[i]x

从而只需要找到离散化以后 s u m [ i ] − x sum[i]-x sum[i]x的位置,然后线段树查询找到 d p [ j ] dp[j] dp[j],相应的树的初值要设为 − ∞ -\infty ,然后插入 s u m [ 0 ] = 0 sum[0]=0 sum[0]=0



以下代码:

#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=2e5+50;
int n,k,N;
LL a[maxn],sum[maxn],b[maxn],ans;
int t[maxn<<2];
void init()
{
    sort(b+1,b+n+2);
    N=unique(b+1,b+n+2)-(b+1);  //离散化后的范围:1~N
    for(int i=0;i<=n;i++)
        sum[i]=lower_bound(b+1,b+N+1,sum[i])-b;
}
void build(int rt,int l,int r)
{
    if(l==r)
    {
        t[rt]=-INF;
        return;
    }
    int mid=(l+r)>>1;
    build(rt<<1,l,mid);
    build(rt<<1|1,mid+1,r);
    t[rt]=max(t[rt<<1],t[rt<<1|1]);
}
void updata(int rt,int l,int r,int p,int val)
{
    if(l==r)
    {
        t[rt]=max(t[rt],val);
        return;
    }
    int mid=(l+r)>>1;
    if(p<=mid)
        updata(rt<<1,l,mid,p,val);
    else
        updata(rt<<1|1,mid+1,r,p,val);
    t[rt]=max(t[rt<<1],t[rt<<1|1]);
}
int query(int rt,int l,int r,int ql,int qr)
{
    if(ql<=l&&r<=qr)
        return t[rt];
    if(r<ql||l>qr)
        return -INF;
    int mid=(l+r)>>1;
    return max(query(rt<<1,l,mid,ql,qr),query(rt<<1|1,mid+1,r,ql,qr));
}
bool check(LL x)
{
    int dp[maxn];
    build(1,1,N);   //初始化树为-INF
    updata(1,1,N,sum[0],0);  //把sum[0]=0放入树中
    for(int i=1;i<=n;i++)
    {
        int pos=lower_bound(b+1,b+N+1,b[sum[i]]-x)-b;   //找到离散化后的位置pos
        if(pos>N)   //如果pos超过N,肯定找不到对应的dp[j],直接跳过
            continue;
        dp[i]=query(1,1,N,pos,N)+1;
        updata(1,1,N,sum[i],dp[i]);
        if(dp[i]>=k)
        {
            ans=min(ans,x);
            return true;
        }
    }
    return false;
}
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d %d",&n,&k);
        b[n+1]=sum[0]=0;    //多加一个sum[0]=0
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&a[i]);
            sum[i]=sum[i-1]+a[i];
            b[i]=sum[i];
        }
        init();   //离散化
        LL L=-1e15,R=1e15,MID;
        ans=LLONG_MAX; //ans记得初始化
        while(L<=R)    //二分
        {
            MID=(L+R)>>1;
            if(check(MID))
                R=MID-1;
            else
                L=MID+1;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值