codeforces round 1054(e.f)
E
想求 长为 \(l\) ~ \(r\) 的区间,且区间内的数字种类 恰好 为 \(k\) 的区间数
由于 恰好为 \(k\) 种数 不好求,所以利用 容斥 思想,转换为 至少 k 种 - 至少 k - 1种
由于区间长度是变化的,双指针使用起来很麻烦,可以利用 前缀和 思想 转换为 长度至少为 r 的区间数 - 长度至少为 l - 1 的区间数
利用双指针实现即可
void yqmr()
{
int n, k, l, r; cin >> n >> k >> l >> r;
vector<int> a(n);
for(int i = 0; i < n; i++) cin >> a[i];
auto f = [&](int l) -> int{ //长度小于等于l,数字种类小于等于k的区间数
if(l == 0) return 0;
auto g = [&](int k) -> int {
if(k == 0) return 0;
int cnt = 0, j = 0, ans = 0;
map<int, int> mp;
for(int i = 0; i < n; i++) {
if(mp[a[i]]++ == 0) cnt++;
while(cnt > k || i - j + 1 > l) {
if(--mp[a[j++]] == 0) cnt--;
}
ans += i - j + 1; //以i为右端点符合条件的区间数
}
return ans;
};
return g(k) - g(k - 1);
};
cout << f(r) - f(l - 1) << '\n';
}
F
总次数=d+休息次数
想让 总次数 最少,就是让 休息次数 最少
休息 是为了分段,减小伤害,避免累积;伤害 越小,就不需要更多的休息次数
想要 伤害越小 ,就要把伤害均分到每一段,一段后休息一次是最优的
void yqmr()
{
int h, d; cin >> h >> d;
int l = 0, r = d;
auto f = [&](int x) {return x * (x + 1) / 2;}; //求长度为x的一段的伤害值
auto check = [&](int x) {
int q = d / (x + 1), r = d % (x + 1); //需要r段(q + 1)长度
return f(q) * (x + 1 - r) + f(q + 1) * r < h + x;
};
while(l <= r) {
int mid = l + r >> 1;
if(check(mid)) r = mid - 1;
else l = mid + 1;
}
cout << d + l << '\n';
}

浙公网安备 33010602011771号