#507. 「LibreOJ NOI Round #1」接竹竿 dp


题目:

题解:

我们考虑把每对花色相同的牌看作区间。
那么如果我们设 \(f_i\) 表示决策在 \([1,i]\) 内的最优答案.
那么有 \(f_i = max\{max\{(f_{j-1}+\sum_{k=j}^iv_k) | a_{j-1} = a_i\},f_{i-1}\}\)
我们可以记录每个点上一次出现的位置 \(la_i\).
那么每次我们更新的时候用 \(la\) 跳转即可。
然后我们发现每个数只能用和它相同的数的位置转移过来。
所以实际上这分成了若干的转移线。
然后我们发现在每条转移线上的转移点是单调的。
并且转移点更新的条件是用下一个地方转移比得到的区间价值和更大。
所以可以做到 \(O(n)\)

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
inline void read(int &x){
	x=0;static char ch;static bool flag;flag =false;
	while(ch=getchar(),ch<'!');if(ch == '-') ch=getchar(),flag = true;
	while(x=10*x+ch-'0',ch=getchar(),ch>'!');if(flag) x=-x;
}
#define rg register int
#define rep(i,a,b) for(rg i=(a);i<=(b);++i)
#define per(i,a,b) for(rg i=(a);i>=(b);--i)
const int maxn = 1000010;
int c[maxn],v[maxn],ws[maxn],la[maxn];
ll s[maxn],f[maxn];int g[maxn];
int main(){
	int n,m;read(n);read(m);
	rep(i,1,n) read(c[i]);
	rep(i,1,n) read(v[i]),s[i] = s[i-1] + v[i];
	rep(i,1,n){
		g[c[i]] = 0;
		la[i] = ws[c[i]];
		ws[c[i]] = i;
	}
	f[0] = 0;
    rep(i,1,n){
		f[i] = f[i-1];
		if(la[i] != 0){
			f[i] = max(f[i],f[g[c[i]]-1]+s[i]-s[g[c[i]]-1]);
			if(f[i-1] - f[g[c[i]]-1] > s[i-1] - s[g[c[i]]-1]) g[c[i]] = i;
		}else g[c[i]] = i;
	}printf("%lld\n",f[n]);
	return 0;
}
posted @ 2017-07-09 06:15  Sky_miner  阅读(611)  评论(0编辑  收藏  举报