洛谷 P11012 颜料

洛谷 P11012 颜料

题意

给出长度为 n 的序列 a。定义一段区间 [l,r] 的绚丽程度 Xl,ri=1Wj=i+1Wmin(ci,cj),其中 W 是值域,ci 表示 a 序列 [l,r]i 出现的次数,求绚丽程度至少为 k 的区间长度最小值。

思路

考虑固定右端点 r,对于 l1<l2Xl1,rXl2,r

考虑固定左端点 l,对于 r1<r2Xl,r1Xl,r2

可以使用双指针求出最短的区间 [l,r]

对于每个 r,若当前 [l,r] 绚丽程度大于 kl 向右移动直到不能再动,统计答案后,r 向右移动。

我们需要一种快速的方式更新区间的绚丽程度。

由于双指针每次只删除或增加一个数,我们考虑如何快速统计增加或删除一个数的贡献。

对于一个数 x,记 s 为小于 x 的数的和,记 C 为在 [x,W] 内的数的个数。

x 对绚丽程度的贡献为 s+(C1)x

因为小于 x 的数和 xmin 结果为本身,大于等于 x 的数和 xmin 结果为 x,减 1 是减去 x 本身。

动态维护这些值可以使用权值树状数组或权值线段树。

加入一个数 num 时,删除 cnum 的贡献,将 cnum 加一,加入 cnum 的贡献。

删除一个数 num 时,删除 cnum 的贡献,将 cnum 减一,加入 cnum 的贡献。

同时动态维护上面的所有值。

开两棵树状数组,一棵维护和,一棵维护个数即可。

时间复杂度:O(nlogn)

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e6 + 5;
int n, W, k, a[N], c[N], val;
template <typename T> inline  void read(T& x) {
	x = 0;
	char ch = getchar();
	while (ch < '0' || ch > '9') ch = getchar();
	while (ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar();
}
struct BIT {
	int t[N];
	void clear() {
		memset(t, 0, sizeof(t));
	}
	void add(int x, int y) {
		for (int i = x; i <= W; i += i & (-i)) t[i] += y;
	}
	int query(int x) {
		int res = 0;
		for (int i = x; i; i -= i & (-i)) res += t[i];
		return res;
	}
	int query(int x, int y) {
		return query(y) - query(x - 1);
	}
} T1, T2;
// T1 维护和
// T2 维护个数
int calc(int num) { // num 的贡献
	int res = 0;
	res += T1.query(num - 1);
	res += num * (T2.query(num, W) - 1);
	return res;
}
void del(int num) { // 删数
	val -= calc(c[num]); // 删除旧贡献
	T1.add(c[num], -c[num]); // 维护树状数组
	T2.add(c[num], -1);
	c[num] --; // 更改 c[num]
	if (c[num]) { // 加入新贡献
		T1.add(c[num], c[num]);
		T2.add(c[num], 1);
		val += calc(c[num]);
	}
}
void add(int num) { // 加数
	if (c[num]) { // 删除旧贡献
		val -= calc(c[num]); // 维护树状数组
		T1.add(c[num], -c[num]);
		T2.add(c[num], -1);
	}
	c[num] ++; // 更改 c[num]
	T1.add(c[num], c[num]); // 加入新贡献
	T2.add(c[num], 1);
	val += calc(c[num]);
}
signed main() {
	read(n), read(k);
	for (int i = 1; i <= n; i ++) read(a[i]), W = max(a[i], W);
	int l = 1, res = 1e9;
	for (int i = 1; i <= n; i ++) {
		add(a[i]); // 加入 i
		while (val >= k) { // 向右移动 l
			del(a[l ++]);
			if (val < k) { // 移不动了
				add(a[-- l]);
				break;	
			}
		}
		if (val >= k) res = min(res, i - l + 1); // 统计答案
	}
	if (res == 1e9) cout << -1;
	else cout << res;
	return 0;
}
posted @   maniubi  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示