题意: n 个数要分成 k 块,每块的价值是其不同数的个数,问价值和最大是多少。
tags: dp[i][j]表示前 j 个数分成 i 块的最大权值和,转移: dp[i][j] = max( dp[i-1][k] + val[k+1][j] ) , k是 1~j 。 但这个过程其实并不好转移,要利用累加的特点,用线段树进行优化 (感觉我不看题解是想不到的,2333)
大概就是,对于第 i 层,我们假定已经知道了第 i-1 层,也就是求出了 dp[i-1][j],现在要求dp[i][j]。遍历 j 从 1~n ,对于第 j 个数,假定 a[j] 上一个位置是 last[j],那我们对 last[j]+1 ~ j 位置都累加一个 1 。这样的话,dp[i-1][k] 就变成了 dp[i][j] ,然后求个区间最值就行了。
#include<bits/stdc++.h> using namespace std; #pragma comment(linker, "/STACK:102400000,102400000") #define rep(i,a,b) for (int i=a; i<=b; ++i) #define per(i,b,a) for (int i=b; i>=a; --i) #define mes(a,b) memset(a,b,sizeof(a)) #define INF 0x3f3f3f3f #define MP make_pair #define PB push_back #define fi first #define se second typedef long long ll; const int N = 35005, M = 55; int n, k, a[N], last[N], pre[N]; ll tr[N<<2], dp[M][N], lazy[N<<2]; void pushdown(int ro, int l, int r) { if(l==r) return ; if(lazy[ro]) { lazy[ro<<1] += lazy[ro]; lazy[ro<<1|1] += lazy[ro]; tr[ro<<1] += lazy[ro]; tr[ro<<1|1] += lazy[ro]; lazy[ro] = 0; } } void update(int ql, int qr, int ro, int l, int r, int c) { if(ql<=l && r<=qr) { lazy[ro]+=c, tr[ro]+=c; return ; } pushdown(ro, l, r); int mid = l+r>>1; if(ql<=mid) update(ql, qr, ro<<1, l, mid, c); if(mid<qr) update(ql, qr, ro<<1|1, mid+1, r, c); tr[ro] = max(tr[ro<<1], tr[ro<<1|1]); } ll query(int ql, int qr, int ro, int l, int r) { if(ql<=l && r<=qr) return tr[ro]; pushdown(ro, l, r); ll ans = 0; int mid = l+r>>1; if(ql<=mid) ans = max(ans, query(ql, qr, ro<<1, l, mid)); if(mid<qr) ans = max(ans, query(ql, qr, ro<<1|1, mid+1, r)); return ans; } void build(int ro, int l, int r, int i) { lazy[ro] = 0, tr[ro] = 0; if(l==r) { tr[ro] = dp[i][l-1]; return ; } int mid = l+r>>1; build(ro<<1, l, mid, i); build(ro<<1|1, mid+1, r, i); tr[ro] = max(tr[ro<<1], tr[ro<<1|1]); } int main() { scanf("%d %d", &n, &k); rep(i,1,n) { scanf("%d", &a[i]); last[i] = pre[a[i]], pre[a[i]] = i; } rep(i,1,k) { build(1, 1, n, i-1); rep(j,1,n) { update(last[j]+1, j, 1, 1, n, 1); dp[i][j] = query(1, j, 1, 1, n); } } printf("%lld ", dp[k][n]); return 0; }