BZOJ 2149 拆迁队

来源:互联网 发布:zookeeper nginx 区别 编辑:程序博客网 时间:2024/04/25 06:42

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

题意: 给定一个数列, 修改其中的一些数,使得这个数列是严格递增的。并要求,修改的数尽量少(保留的数尽量多),并且代价尽量小。
代价的计算公式:修改完后所有数的和 + 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的转移方程(gii 此时的 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;}
1 1
原创粉丝点击