此题题意我一次读不清 , 是不是要完……
题意: 给定一个数列, 修改其中的一些数,使得这个数列是严格递增的。并要求,修改的数尽量少(保留的数尽量多),并且代价尽量小。
代价的计算公式:修改完后所有数的和 +
∑ibi,数i没被修改
第一问:
令
di=ai−i
那么问题等价于求数列
d
的最长不下降子序列。原因是这里要求严格递增, 而且数列中的每一项都不能删除,所以要留足够的位置给中间的数。
令
第二问:
显然这是个
我们按照
f
的大小分层
我们可以用数据结构或者分治的方式来处理, 但是由于我们不能用一个集合
Sj
去集中更新
i
所以,到此为止我们算法的时间复杂度依然是
我们再观察
dp
的转移方程(
gi
是
i
此时的
我们令:
于是我们可以维护一个下凸包。
然而接下来的工作就看你如何处理二维偏序了, 如果你选择分治,可以先按下标排序,分治中再按
d
排序, 每次更新时二分斜率或者三分值就可以啦。
时间复杂度可以是
如果你选择维护可持久化凸包的话,可以先按
d
排序, 然后用可持久化线段树维护这玩意,每次依然二分,这样可以做到
给一个思路清晰的 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;
}