ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

CF833B The Bakery 题解

2022-04-29 20:32:43  阅读:148  来源: 互联网

标签:Pre CF833B val int 题解 Bakery MAXN fir sec


一道简单 ds 优化 dp 题。

朴素方程:设 \(f_{i,j}\) 表示将 \([1,i]\) 内的所有数划分成 \(j\) 段的方案数,那么有转移方程:

\[f_{i,j}=\max\{f_{k,j}+val(j+1,i) \mid k - 1 \le j < i\} \]

也就是将 \([j+1,i]\) 划成一段,\(val(j+1,i)\) 表示 \([j+1,i]\) 内不同 \(a_i\) 数量,复杂度 \(O(n^3k)\),如果 \(j\) 倒序枚举可以做到 \(O(n^2k)\),考虑优化。

先预处理一个 \(Pre_i\) 表示 \(a_{Pre_i}=a_i\) 且 \(Pre_i\) 最大,那么只有满足 \(j \in [Pre_i+1,i]\) 的 \(val(j+1,i)\) 会被影响到,有个 1 的贡献。

据此我们考虑先枚举 \(j \in [1,k]\) 为划分段数,然后对于 \(f_{i,j-1}\) 建线段树,线段树区间中 \(x\) 位置表示 \(f_{x,j-1}\)。

根据上文所述,\(i\) 会影响到 \(j \in [Pre_i+1,i]\) 的 dp 值,于是我们先对区间 \([Pre_i,i-1]\) 区间加 1,左右端点减 1 的原因是转移方程的 \(val\) 中 \(j\) 加了 1。

然后对于 \(f_{i,j}\) 查询 \([j-1,i-1]\)(注意左端点不能是 1)内的最大值即可。

答案 \(f_{n,k}\),初值全 0,复杂度 \(O(nk \log n)\),没必要滚动数组。

GitHub:CodeBase-of-Plozia

Code:

/*
========= Plozia =========
	Author:Plozia
	Problem:CF833B The Bakery
	Date:2022/4/29
========= Plozia =========
*/

#include <bits/stdc++.h>

typedef long long LL;
const int MAXN = 3.5e4 + 5;
int n, k, a[MAXN], f[MAXN][55], Pre[MAXN], book[MAXN];
struct node
{
	int tag, Maxn;
}tree[MAXN << 2];
#define tag(p) tree[p].tag
#define Maxn(p) tree[p].Maxn

int Read()
{
	int sum = 0, fh = 1; char ch = getchar();
	for (; ch < '0' || ch > '9'; ch = getchar()) fh -= (ch == '-') << 1;
	for (; ch >= '0' && ch <= '9'; ch = getchar()) sum = sum * 10 + (ch ^ 48);
	return sum * fh;
}
int Max(int fir, int sec) { return (fir > sec) ? fir : sec; }
int Min(int fir, int sec) { return (fir < sec) ? fir : sec; }

void Update(int p) { Maxn(p) = Max(Maxn(p << 1), Maxn(p << 1 | 1)); }
void Spread(int p)
{
	if (tag(p))
	{
		Maxn(p << 1) += tag(p); Maxn(p << 1 | 1) += tag(p);
		tag(p << 1) += tag(p); tag(p << 1 | 1) += tag(p);
		tag(p) = 0;
	}
}

void Change(int p, int x, int v, int lp, int rp)
{
	tag(p) = 0; if (lp == rp) { Maxn(p) = v; return ; }
	int mid = (lp + rp) >> 1;
	if (x <= mid) Change(p << 1, x, v, lp, mid);
	else Change(p << 1 | 1, x, v, mid + 1, rp);
	Update(p);
}
void Add(int p, int l, int r, int v, int lp, int rp)
{
	if (lp >= l && rp <= r) { Maxn(p) += v; tag(p) += v; return ; }
	Spread(p); int mid = (lp + rp) >> 1;
	if (l <= mid) Add(p << 1, l, r, v, lp, mid);
	if (r > mid) Add(p << 1 | 1, l, r, v, mid + 1, rp);
	Update(p);
}
int Ask(int p, int l, int r, int lp, int rp)
{
	if (lp >= l && rp <= r) return Maxn(p);
	Spread(p); int mid = (lp + rp) >> 1, val = 0;
	if (l <= mid) val = Max(val, Ask(p << 1, l, r, lp, mid));
	if (r > mid) val = Max(val, Ask(p << 1 | 1, l, r, mid + 1, rp));
	return val;
}

int main()
{
	n = Read(), k = Read(); for (int i = 1; i <= n; ++i) a[i] = Read();
	for (int i = 1; i <= n; ++i)
	{
		if (!book[a[i]]) book[a[i]] = i;
		else { Pre[i] = book[a[i]]; book[a[i]] = i; }
	}
	for (int j = 1; j <= k; ++j)
	{
		for (int i = 1; i <= n; ++i) Change(1, i, f[i][j - 1], 0, n);
		for (int i = 1; i <= n; ++i)
		{
			Add(1, Pre[i], i - 1, 1, 0, n);
			f[i][j] = Ask(1, j - 1, i - 1, 0, n);
		}
	}
	printf("%d\n", f[n][k]); return 0;
}

标签:Pre,CF833B,val,int,题解,Bakery,MAXN,fir,sec
来源: https://www.cnblogs.com/Plozia/p/16208129.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有