BZOJ 2149 拆迁队

3 篇文章 0 订阅
2 篇文章 0 订阅

此题题意我一次读不清 , 是不是要完……

题意: 给定一个数列, 修改其中的一些数,使得这个数列是严格递增的。并要求,修改的数尽量少(保留的数尽量多),并且代价尽量小。
代价的计算公式:修改完后所有数的和 + ibi,i

第一问:
di=aii 那么问题等价于求数列 d 的最长不下降子序列。原因是这里要求严格递增, 而且数列中的每一项都不能删除,所以要留足够的位置给中间的数。
fi 表示 1 ~ i 中保留数 i 的前提下最多保留多少个数 ,这是个经典问题。

第二问:
显然这是个 dp 问题,我们先来看看怎样的 j 能够更新 i

j<i

djdi

fj+1=fi

我们按照 f 的大小分层 dp , 摆在我们面前的是个二维偏序问题。
我们可以用数据结构或者分治的方式来处理, 但是由于我们不能用一个集合 Sj 去集中更新 i 所以,到此为止我们算法的时间复杂度依然是 O(n2)

我们再观察 dp 的转移方程( gi i 此时的 dp 值)

g[i]=min{d[j]i+g[j]A[j]jA[j]+j×(j+1)2}+i×(i1)2+A[i]+B[i]
(摘自 这个小伙伴的博客

我们令:

xj=dj,yj=g[j]A[j]jA[j]+j×(j+1)2

于是我们可以维护一个下凸包。
然而接下来的工作就看你如何处理二维偏序了, 如果你选择分治,可以先按下标排序,分治中再按 d 排序, 每次更新时二分斜率或者三分值就可以啦。
时间复杂度可以是 O(nlog(n)2) 或者 O(nlog(n)3)

如果你选择维护可持久化凸包的话,可以先按 d 排序, 然后用可持久化线段树维护这玩意,每次依然二分,这样可以做到 O(nlog(n)2)

给一个思路清晰的 O(nlog(n)3) 的分治代码 , 换成归并排序即为 O(nlog(n)2) :

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const ll maxn = 1e5+1e2;
const ll INF = 0x3f3f3f3f3f3f3f3f;

ll n , d[maxn] , f[maxn] , len[maxn] , ori[maxn]; ll g[maxn] , a[maxn] , b[maxn];
vector<int> level[maxn];

ll *ss;
struct data
{
    ll k , id; ll x , y;
    data(){}
    data(ll k , ll id):k(k),id(id) { x = d[id]; y = g[id] - a[id]*id - a[id] + (id+1)*id/2; }
    bool operator <(const data& b) const { return ss[id] < ss[b.id] || (ss[id] == ss[b.id] && !k); } 
}q[maxn]; ll m;

void update(vector<data>& c , ll i)
{
    static ll l , r , len , ml , mr;
    l = 0 , r = (int)c.size()-1;
    while(l+4 < r)
    {
        len = (r-l)/3; ml = l + len; mr = r - len;
        if(c[ml].x*i+c[ml].y > c[mr].x*i+c[mr].y) l = ml;
        else r = mr;
    }
    while(l <= r) g[i] = min(g[i] , c[l].x*i+c[l].y) , ++l;
}

void solve(ll l , ll r)
{
    if(l == r) { if(q[l].k) { ll x = q[l].id; g[x] += a[x] + b[x] + (x-1)*x/2; } return; }

    ll mid = (l+r)/2;
    solve(l , mid);
    vector<data> qt , con;
    for(ll i=l;i<=mid;i++) if(!q[i].k) qt.push_back(q[i]);
    for(ll i=r;i>mid;i--)   if(q[i].k) qt.push_back(q[i]);

    sort(qt.begin() , qt.end());
    for(ll i=0;i<qt.size();i++) 
    {
        data& j = qt[i];
        if(j.k) update(con , j.id);
        else 
        {
            ll t = (int)con.size()-1;

            while(t>=1 && 1.0*(j.y - con[t].y) * (con[t].x - con[t-1].x) < 1.0*(con[t].y - con[t-1].y) * (j.x - con[t].x)) con.pop_back() , --t;
            con.push_back(j);
        }
    }
    solve(mid+1 , r);
}

int main()
{
    #ifndef ONLINE_JUDGE
    freopen("in","r",stdin);
    #endif

    cin>>n;
    for(ll i=1;i<=n;i++) scanf("%lld" , a+i) , d[i] = a[i] - i , ori[i] = i;
    for(ll i=1;i<=n;i++) scanf("%lld" , b+i);
    for(ll i=1;i<=n;i++) len[i] = INF , g[i] = INF; 

    for(ll i=1;i<=n;i++) if(a[i] >= i)
    {
        f[i] = upper_bound(len+1 , len+1+n , d[i]) - len;
        level[f[i]].push_back(i);
        len[f[i]] = min(len[f[i]] , d[i]);
    }

    level[0].push_back(0);
    for(ll i=1;;i++)
    {
        if(level[i].empty()) 
        {
            ll res = INF;
            for(ll j=0;j<level[i-1].size();j++) 
            {
                ll k = level[i-1][j];
                res = min(res , g[k] + (a[k]*2 + 1 + n - k)*(n-k)/2);
            }
            cout<<i-1<<" "<<res<<endl; break;
        }

        m = 0;
        for(ll j=0;j<level[i-1].size();j++) q[++m] = data(0 , level[i-1][j]);
        for(ll j=0;j<level[i].size();j++) q[++m] = data(1 , level[i][j]);

        ss = ori; sort(q+1 , q+1+m); ss = d;
        solve(1 , m);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值