删区间
(remove.cpp/c)
【问题描述】
给出一个长度为?的数组?,你每次需要选出一个长度大于1的区间[?, ?]并删掉它,代价
是左右端点的元素之差的绝对值|?? − ??
|,之后再将左右两个数组接起来构成一个新的数组。
你的任务是要求出删除整个数组的最小代价和。
【输入格式】
输入文件名为 remove.in。
第一行输入一个正整数?。
接下来一行输入?个整数表示数组?。
【输出格式】
输出文件名为 remove.out。
输出一行表示最小代价和。
好题。
\(n^3\)区间DP很好想。
尝试发掘一些性质,可以发现,我们选出的区间一定不会存在包含关系,那么就可以优化DP,也就是可以固定左端点为1。
\[ f(i)=min(f(i),f(j-1)+abs(a(i)-a(j))) \]
这样就可以得到70分了。
考虑用数据结构优化DP。
先把DP式子拆开:
\[ f(i)=min(f(i),f(j-1)+a(i)-a(j))-->a(i)>=a(j); \\=min(f(i),f(j-1)+a(j)-a(i))-->a(i)<a(j) \]
可以发现出现了两个变量,分别是\(f(j-1)+a(j)\)和\(f(j-1)-a(j)\)。
那么对于\(a(i)\),会有两种情况的\(j\),于是可以想到用两个树状数组维护上述两个变量的前缀后缀最小值就可以了就可以了。
还有一个地方需要注意,对于\(a(j)>a(i)\)的情况,我们要查询后缀最小值,那么可以在查询和插入的时候将树状数组翻转。%%%雷哥的黑科技。
总之收获很大,也意识到要多做树状数组题了。
code:
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#define int long long
using namespace std;
const int lei_zi=500017;
inline int read(){
int sum=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1; ch=getchar();}
while(ch>='0'&&ch<='9'){sum=(sum<<1)+(sum<<3)+ch-'0'; ch=getchar();}
return sum*f;
}
int s[lei_zi],t[lei_zi];
int a[lei_zi],b[lei_zi];
int f[lei_zi];
int n;
int Abs(int x){
if(x<0)return -x;
return x;
}
void add1(int pos,int k){
for(int i=pos;i<=n;i+=(i&-i)){
s[i]=min(s[i],k);
}
}
void add2(int pos,int k){
pos = n - pos + 1;
for(int i=pos;i<=n;i+=(i&-i)){
t[i]=min(t[i],k);
}
}
int query1(int pos){
int re=3038287259199220266LL;
for(int i=pos;i>=1;i-=(i&-i)){
re=min(re,s[i]);
}
return re;
}
int query2(int pos){
pos=n-pos+1;
int re=3038287259199220266LL;
for(int i=pos;i>=1;i-=(i&-i)){
re=min(re,t[i]);
}
return re;
}
signed main(){
freopen("remove.in","r",stdin);
freopen("remove.out","w",stdout);
n=read();
for(int i=1;i<=n;i++)
a[i]=read(),b[i]=a[i];
memset(s,127/3,sizeof s);
memset(t,127/3,sizeof t);
sort(b+1,b+1+n);
for(int i=1;i<=n;i++)
a[i]=lower_bound(b+1,b+1+n,a[i])-b;
f[2]=Abs(b[a[2]]-b[a[1]]); f[3]=Abs(b[a[3]]-b[a[1]]);
add1(a[3],f[2]-b[a[3]]) , add2(a[3],f[2]+b[a[3]]);
for(int i=4;i<=n;i++){
f[i]=Abs(b[a[i]]-b[a[1]]);
int tmp1=query1(a[i]);
int tmp2=query2(a[i]);
f[i]=min(f[i],tmp1+b[a[i]]);
f[i]=min(f[i],tmp2-b[a[i]]);
add1(a[i],f[i-1]-b[a[i]]); add2(a[i],f[i-1]+b[a[i]]);
}
printf("%lld\n",f[n]);
fclose(stdin);
fclose(stdout);
return 0;
}