zoukankan      html  css  js  c++  java
  • 洛谷 P2501 [HAOI2006]数字序列 解题报告

    P2501 [HAOI2006]数字序列

    题目描述

    现在我们有一个长度为n的整数序列A。但是它太不好看了,于是我们希望把它变成一个单调严格上升的序列。但是不希望改变过多的数,也不希望改变的幅度太大。

    输入输出格式

    输入格式:

    第一行包含一个数n,接下来n个整数按顺序描述每一项的键值。

    输出格式:

    第一行一个整数表示最少需要改变多少个数。

    第二行一个整数,表示在改变的数最少的情况下,每个数改变的绝对值之和的最小值。

    说明

    90%的数据n<=6000。

    100%的数据n<=35000。

    保证所有数列是随机的。


    “数据随机”==乱搞 啊哈

    陷入了笛卡尔树的坑里

    看了题解,大家一致认为第一问灰常简单,第二问灰常毒瘤

    我:

    好吧,第一问其实有思想的,发现直接求要改变的死活不好弄,不妨使用补集转换的思想,求最多不改变的数字

    (dp_i)代表以(i)为末尾的数字不改变时的最大不改变数字

    转移有:

    (dp_i=max_{a_i-a_j ge i-j} dp_j +1)

    复杂度是(O(N^2))

    我们发现,其实我们是在最大化转移次数

    如果把转移条件移项(a_i-i ge a_j-j)

    (b_i=a_i-i),问题就转换成了求(LIS),可以(O(nlogn))求解

    第二问 有点微妙 实质上是一个跑不满的(O(N^3))做法,上界极其宽松(当然要写的好才行)

    (a)变得单调上升,等价与把(b)变的单调不降,花费是等价的

    (f_i)为把前(i)项合法的最小花费

    转移有:

    (f_i=min_{dp_i==dp_j+1} f_j+cost_{i,j})

    先不考虑如何计算花费,考虑卡枚举前一维的常数

    很显然前(i)项是要跑满的,从哪里转移我们建一个链表就表示转移集合

    考虑如何计算费用

    发现如果可以转移,那所有的在(b_i)(b_i)之间的(b)没有值是夹在它们中间的。

    它们一定会往两端进行靠拢,可以证明(没看懂原证明),存在一个(k),使(b_i)~(b_k)都为(b_i),使(b_k+1)~(b_j)都为(b_j)

    所有我们只需要枚举中间的这个(k)就行啦

    代码细节还是很多的,没给值域还是很坑的


    Code:

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <vector>
    #define ll long long
    ll min(ll x,ll y){return x<y?x:y;}
    ll abs(ll x){return x>0?x:-x;}
    const ll N=35002;
    ll n,a[N],b[N],g[N],k;
    std::vector <ll > dx[N];ll dp[N],s1[N],s2[N];
    int main()
    {
        scanf("%lld",&n);dp[0]=-0x3f3f3f3f,b[n+1]=-dp[0],b[0]=dp[0];
        for(ll i=1;i<=n;i++)
            scanf("%lld",a+i),b[i]=a[i]-i;
        for(ll i=1;i<=n;i++)
        {
            if(b[i]>=dp[k]) dp[++k]=b[i],g[i]=k;
            else
            {
                g[i]=std::upper_bound(dp+1,dp+1+k,b[i])-dp;
                dp[g[i]]=b[i];
            }
        }
        printf("%lld
    ",n-k);g[++n]=k+1;
        for(ll i=0;i<=n;i++) dx[g[i]].push_back(i);
        memset(dp,0x3f,sizeof(dp));
        dp[0]=0;
        for(ll i=1;i<=n;i++)
        {
            for(ll j=0;dx[g[i]-1][j]<i&&j<dx[g[i]-1].size();j++)
            {
                ll to=dx[g[i]-1][j];
                if(b[i]<b[to]) continue;
                for(ll l=to;l<=i;l++) s1[l]=abs((ll)(b[l]-b[to])),s2[l]=abs((ll)(b[l]-b[i]));
                for(ll l=to+1;l<=i;l++) s1[l]+=s1[l-1],s2[l]+=s2[l-1];
                for(ll l=to;l<=i;l++)
                    dp[i]=min(s1[l]-s1[to]+s2[i]-s2[l]+dp[to],dp[i]);
            }
        }
        printf("%lld
    ",dp[n]);
        return 0;
    }
    
  • 相关阅读:
    laydate 监听日期切换
    done
    Could not find result map java.util.HashMap
    toFixed
    js追加元素
    input只能输入数字或两位小数
    JSTree[树形控件]
    JSp获取到当前用户的全部session
    layui select change
    大型网站技术架构读后感
  • 原文地址:https://www.cnblogs.com/butterflydew/p/9560372.html
Copyright © 2011-2022 走看看