洛谷 P2300 合并神犇 解题报告

P2300 合并神犇

题目背景

loidc来到了NOI的赛场上,他在那里看到了好多神犇。

题目描述

神犇们现在正排成一排在刷题。每个神犇都有一个能力值p[i]。loidc认为坐在附近的金牌爷能力参差不齐非常难受。于是loidc便想方设法对神犇们进行人道主义合并。

loidc想把神犇的能力值排列成从左到右单调不减。他每次可以选择一个神犇,把他合并到两侧相邻的神犇上。合并后的新神犇能力值是以前两位犇的能力值之和。每次合并完成后,被合并的两个神犇就会消失。合并后的新神犇不能再分开(万一他俩有女朋友咋办)因此每次合并后神犇的总数会减1.

loidc想知道,想治好他的强迫症需要合并多少次

输入输出格式

输入格式:

第一行一个整数 n。

第二行 n 个整数,第 i 个整数表示 p[i]。

输出格式:

loidc需要合并的次数

说明

对于 50%的数据,0< n <=5000。

对于 100%的数据,0< n <=200000,0< p[i] <=2147483647,p 均为随机生成。


暴力数据结构万岁!!!

有一个纯贪心做法,详见大佬的博客

这里我们用暴力数据结构维护。

不难想到转移方程:
\(dp[i]\)代表前\(i\)个人合并后的最小合并次数,\(f[i]\)代表前\(i\)个人在最小合并下的末尾元素,\(sum[i]\)为前缀和数组。

值得一提的是,这里有个贪心:即\(f[i]\)\(dp[i]\)下一定取到最小。

转移:\(dp[i]=dp[k]+i-1-k,f[k]<=sum[i]-sum[k]\)

枚举\(k\)的话是\(O(n^2)\),这里搞个暴力数据结构维护一下。

将转移条件变形,\(sum[k]+f[k]<=sum[i]\),也就是说,我们每次询问值比\(sum[i]\)小的区间所代表的转移中的最小值即可。

在线段树上维护二元组\((dp[k]+n-k,k)\),第一组为第一关键字,求最小值,第二组为第二关键字,求最大值。

为什么求最大值?一个贪心,当\(dp[k]+n-k\)相等时,\(k\)越往后取,\(f[i]\)越小(或不变)。

然而区间可能开的很大,我们得想办法优化一下空间。

参照主席树的思想进行离散,我们可以对用不上的区间先不管,询问时或者改变时再加上即可。


code:

#include <cstdio>
#define LS t[id].ls
#define RS t[id].rs
#define Mid (ql+qr>>1)
typedef long long ll;
const int N=200010;
ll a[N],s[N],f[N];
int n,dp[N],root;
//dp[i]长度为i的合法序列最小合并
//f[i]长度为i的合法序列最小合并下的末尾最小大小
struct node2
{
    int c,pos;
    bool friend operator <(node2 n1,node2 n2)
    {
        if(n1.c!=n2.c) return n1.c<n2.c;
        return n1.pos>n2.pos;
    }
}inf;
node2 min(node2 x,node2 y){return x<y?x:y;}
struct node
{
    int ls,rs;//子树
    node2 c;
}t[30000002];//维护值在l,r区间内的最小dp[k]+n-k;
int tot=0,pos[N];
int add(ll l,ll r,node2 x)
{
    t[++tot].c=x;
    return tot;
}
node2 query(int id,ll ql,ll qr,ll l,ll r)
{
    if(!id) return inf;
    if(l==ql&&r==qr)
        return t[id].c;
    if(r<=Mid) return query(LS,ql,Mid,l,r);
    else if(l>Mid) return query(RS,Mid+1,qr,l,r);
    else return min(query(LS,ql,Mid,l,Mid),query(RS,Mid+1,qr,Mid+1,r));
}
int change(int id,ll ql,ll qr,ll x,node2 del)
{
    if(!id) id=add(ql,qr,del);
    if(ql==qr) return id;
    if(Mid>=x) LS=change(LS,ql,Mid,x,del);
    else RS=change(RS,Mid+1,qr,x,del);
    t[id].c=min(t[id].c,del);
    return id;
}
int main()
{
    scanf("%d",&n);
    inf.c=0x3f3f3f3f;
    inf.pos=0x3f3f3f3f;
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",a+i);
        s[i]=s[i-1]+a[i];
    }
    node2 cc;
    cc.c=n,cc.pos=0;
    root=add(0,s[n],cc);
    change(root,0,s[n],0,cc);
    pos[n]=0;
    for(int i=1;i<=n;i++)
    {
        node2 j=query(1,0,s[n],0,s[i]);
        dp[i]=j.c-1+i-n;
        f[i]=s[i]-s[j.pos];
        if(s[i]+f[i]<=s[n])
        {
            node2 tt;
            tt.c=dp[i]+n-i;
            tt.pos=i;
            change(root,0,s[n],s[i]+f[i],tt);
        }
    }
    printf("%d\n",dp[n]);
    return 0;
}


2018.6.8

posted @ 2018-06-08 22:57  露迭月  阅读(263)  评论(0编辑  收藏  举报