jzo1931 【2010集训队出题】神奇的K线 (dp)

不错的dp题。

题意

给出长度为n-1的序列p和长度为n的序列a,在a中删除或将某些位置的值改变,使得a差分后是p的一个前缀。 删除和修改分别有代价,并且一次修改可以修改为任意值。
n <= 1500

盲目分析

先令 b b 序列是前缀和并往右位移后的p序列。这样就是要找a’序列匹配b序列的权值相对位置(允许整体加减)

观察发现a至少有一个位置被留下来并且没有修改。
基础的暴力的话,可以通过枚举这个位置和他所匹配的b序列位置,做一个 O ( n 2 ) O(n ^ 2) 的dp,这能获得20分的好成绩。

上面的做法很难继续优化了。考虑一个新dp状态
f [ i ] [ j ] f[i][j] 表示a序列决策到第i个,b序列匹配到第j个的最多不操作数个数(在j个数中)
知道 i , j i, j 就可以计算出 1.. i 1..i 中被删除和被修改的个数。 i + 1.. n i + 1.. n 这一段要么全部修改,要么全部删除。
然后通过枚举相邻不操作点来转移

f [ i ] [ j ] f[i&#x27;][j&#x27;] 可以转移到 f [ i ] [ j ] f[i][j] 的条件是:
i &lt; i i&#x27; &lt; i
j &lt; j j&#x27; &lt; j
i i j j i - i&#x27; \ge j - j&#x27; (中间的点个数要够)
a [ i ] b [ j ] = a [ i ] b [ j ] a[i] - b[j] = a[i&#x27;] - b[j&#x27;] (与对应位置的差要一致)

第四个条件非常特殊,所以可以先对状态按这个排序,只考虑相等的部分:

前三个条件乍一看是三维偏序,比较naive的就立马上cdq分治了。但是显然他有特殊性。

用排序先满足一维条件,让 j &lt; j j&#x27; &lt; j 。然后我们发现只要 i i j j i - i&#x27; \ge j - j&#x27; 成立,那么必然有 i &gt; i i &gt; i&#x27; . 因此我们得到了一个 O ( n 2 log n ) O(n ^2 \log n) 的做法。

#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;
}

发布了266 篇原创文章 · 获赞 93 · 访问量 8万+

猜你喜欢

转载自blog.csdn.net/jokerwyt/article/details/98886020