不错的dp题。
题意
给出长度为n-1的序列p和长度为n的序列a,在a中删除或将某些位置的值改变,使得a差分后是p的一个前缀。 删除和修改分别有代价,并且一次修改可以修改为任意值。
n <= 1500
盲目分析
先令 序列是前缀和并往右位移后的p序列。这样就是要找a’序列匹配b序列的权值相对位置(允许整体加减)
观察发现a至少有一个位置被留下来并且没有修改。
基础的暴力的话,可以通过枚举这个位置和他所匹配的b序列位置,做一个
的dp,这能获得20分的好成绩。
上面的做法很难继续优化了。考虑一个新dp状态
设
表示a序列决策到第i个,b序列匹配到第j个的最多不操作数个数(在j个数中)
知道
就可以计算出
中被删除和被修改的个数。
这一段要么全部修改,要么全部删除。
然后通过枚举相邻不操作点来转移
可以转移到
的条件是:
(中间的点个数要够)
(与对应位置的差要一致)
第四个条件非常特殊,所以可以先对状态按这个排序,只考虑相等的部分:
前三个条件乍一看是三维偏序,比较naive的就立马上cdq分治了。但是显然他有特殊性。
用排序先满足一维条件,让 。然后我们发现只要 成立,那么必然有 . 因此我们得到了一个 的做法。
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#define lowbit(x) ((x) & -(x))
using namespace std;
const int N = 1510;
int f[N][N], a[N], b[N], cdel, cmod, p[N];
int n, tot;
pair<int,pair<int,int> > st[N * N];
int t[N];
int query(int x) {
x++;
int ret = 0;
for(; x; x-=lowbit(x)) ret = max(ret, t[x]);
return ret;
}
void set(int x, int v, int force) {
x++;
for(; x <= n + 1; x += lowbit(x))
if(force) t[x] = v;
else t[x] = max(t[x], v);
}
int main() {
freopen("c.in","r",stdin);
cin>>n>>cmod>>cdel;
for(int i = 2; i <= n; i++) scanf("%d",&p[i]), p[i] += p[i - 1];
for(int i = 1; i <= n; i++) scanf("%d",&a[i]);
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= i; j++) {
st[++tot] = make_pair(a[i] - p[j], make_pair(j, -i));
}
}
sort(st + 1, st + 1 + tot);
for(int l = 1; l <= tot; l++) {
int r = l;
while(r <= tot && st[r + 1].first == st[l].first) r++;
for(int i = l; i <= r; i++) {
int x = -st[i].second.second, y = st[i].second.first;
f[x][y] = max(1, query(x - y) + 1);
set(x - y, f[x][y], 0);
}
for(int i = l; i <= r; i++) {
int x = -st[i].second.second, y = st[i].second.first;
set(x - y, 0, 1);
}
l = r;
}
int ans = 2e9;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= i; j++) {
ans = min(ans, (j - f[i][j]) * cmod + (i - j) * cdel + (n - i) * min(cdel, cmod));
}
}
cout<<ans<<endl;
}