Codeforces Round #426(Div.2)D题 DP+线段树
来源:互联网 发布:植物大战僵尸 for mac 编辑:程序博客网 时间:2024/06/16 03:10
题意:
题目链接:http://codeforces.com/contest/834/problem/D
给出n个数,分割成k个区间,每个区间的价值为该区间内不同数字的种类数,问分割之后,所有区间最大价值和是多少。
思路:
很容易得到方程:
dp[i][j]=max(dp[i-1][k]+kind(k+1,j)
其中dp[i][j]表示的是到第j个数为止已经分了i组的最大价值,k的变化范围是[i,j](想想就能明白),这方程要直接计算复杂度肯定不够。
这里需要用到线段树来优化,思考之后可以发现,当遍历到a[j]的时候,若a[j]上一次出现的位置是last[a[j]],那么a[j]会给起点为[last[a[j]] + 1,j],终点为j的每个区间都增加1的贡献,这一步可以利用线段树区间更新,若线段树上初始存的是i-1轮的dp结果,然后从左到右遍历的过程中每次将新的贡献更新到线段树上,就可以维护max(dp[i-1][k]+kind(k+1,j)
,直接query(i,j)即可得到dp[i][j]。
需要注意的地方是:每次更新贡献区间是[last[a[j]] + 1,j],但这贡献是属于last[a[j]到j-1的dp值,所以要更新的是[last[a[j]],j-1]。并且为了不让线段树区间从0开始,这里可以把last初值设置成1,对结果没有影响。
代码:
#include <bits/stdc++.h>using namespace std;#define lson l, m, rt << 1#define rson m + 1, r, rt << 1 | 1const int MAXN = 35005;int a[MAXN], dp[55][MAXN], last[MAXN];int maxx[MAXN << 2], lazy[MAXN << 2];void pushup(int rt) { maxx[rt] = max(maxx[rt << 1], maxx[rt << 1 | 1]);}void pushdown(int rt, int len) { if (lazy[rt]) { lazy[rt << 1] += lazy[rt]; lazy[rt << 1 | 1] += lazy[rt]; maxx[rt << 1] += lazy[rt]; maxx[rt << 1 | 1] += lazy[rt]; lazy[rt] = 0; }}void update(int s, int e, int z, int l, int r, int rt) { if (s <= l && r <= e) { maxx[rt] += z; lazy[rt] += z; return; } pushdown(rt, r - l + 1); int m = (l + r) >> 1; if (s <= m) update(s, e, z, lson); if (e > m) update(s, e, z, rson); pushup(rt);}int query(int s, int e, int l, int r, int rt) { if (s <= l && r <= e) return maxx[rt]; pushdown(rt, r - l + 1); int m = (l + r) >> 1; int ret = 0; if (s <= m) ret = max(ret, query(s, e, lson)); if (e > m) ret = max(ret, query(s, e, rson)); return ret;}bool vis[MAXN];int main() { // freopen("in.txt", "r", stdin); int n, k; scanf("%d%d", &n, &k); for (int i = 1; i <= n; i++) scanf("%d", &a[i]); int kind = 0; memset(vis, false, sizeof(vis)); for (int i = 1; i <= n; i++) { if (!vis[a[i]]) ++kind; dp[1][i] = kind; vis[a[i]] = true; } for (int i = 2; i <= k; i++) { memset(lazy, 0, sizeof(lazy)); memset(maxx, 0, sizeof(maxx)); for (int j = 1; j <= n; j++) { update(j, j, dp[i - 1][j], 1, n, 1); last[a[j]] = 1; } for (int j = 1; j < i; j++) last[a[j]] = j; for (int j = i; j <= n; j++) { //printf("(%d, %d)\n", last[a[j]] + 1, j); update(last[a[j]], j - 1, 1, 1, n, 1); dp[i][j] = query(i - 1, j - 1, 1, n, 1); last[a[j]] = j; } } printf("%d\n", dp[k][n]); return 0;}
阅读全文
0 0
- Codeforces Round #426 (Div. 2) D. The Bakery(DP+线段树) 好题
- Codeforces Round #426(Div.2)D题 DP+线段树
- Codeforces Round #426 (Div. 2) D. The Bakery(DP+线段树)
- Codeforces Round #426 (Div. 2) D. The Bakery(DP+线段树)
- Codeforces Round #426 (Div. 2) D. The Bakery(DP+线段树维护)
- Codeforces Round #426 (Div. 2) D. The Bakery DP,线段树
- 贪心(最大团)||dp+线段树(Codeforces Round #296 (Div. 2)D. Clique Problem)
- Codeforces Round #343 (Div. 2) D. Babaei and Birthday Cake(线段树+dp)
- Codeforces Round #275 (Div. 2) D题 (线段树)
- 线段树(good)Codeforces Round #275 (Div. 2)D
- Codeforces Round #365 (Div. 2) D (线段树)
- Codeforces Round #426 (Div. 2) The Bakery 线段树+dp
- Codeforces Round #343 (Div. 2) D. Babaei and Birthday Cake(线段树+离散化优化DP)
- Codeforces Round #426 (Div. 2)-The Bakery(线段树+DP)
- Codeforces Round #271 (Div. 2) E题 Pillars(线段树维护DP)
- Codeforces Round #197 (Div. 2)(d线段树)
- Codeforces Round #275 (Div. 2) D 线段树
- Codeforces Round #353 (Div. 2) E 线段树+dp
- LaTeX编写分段函数
- #java学习之序列化
- vim taglist
- import cv2 失败 ImportError:DLL load fail:找不到指定模块
- Spring
- Codeforces Round #426(Div.2)D题 DP+线段树
- leetcode11
- LabVIEW视觉里一些算子参数解释
- 文章标题
- android,java知识点总结(一)
- codeforces 834C (The Meaningless Game)
- 小端格式和大端格式(Little-Endian&Big-Endian)
- 【Java并发编程】线程池
- WM基本功能