Codeforces Round #822 (Div. 2) C Removing Smallest Multiples(复杂度为调和级数级别的暴力)

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题意:

给你一个 集合 S 这个 集合里的元素是 1 ~ n,每次操作可以 选择一个数 k,并删除 集合 s 中 最小的 k 的倍数,举个例子,假设一个集合为 {2, 3},里面的元素 均为 1 的倍数,删除的时候只能 先删 2 后删除 3不能跳过 2 再删除 3

给定 集合 T 为 集合 S 的子集,问能不能通过 上面的 删除操作 从集合 S 转化成集合 T,集合的形式以 二进制的形式 给出。

对于题目给定的样例,0010,表示 S 集合为 {1, 2, 3, 4}T 集合为 {3},正确的做法应当是:先选择 k = 1,先后删掉 1、2,之后选择 k = 2,删掉 4,总代价为 4

不符合题意的错误做法选择 k = 1,直接删除 1、2、4,总代价为 3。因为 不能跳过 3 直接删除 4不能跳着删除 数字,每次只能 删掉最小的 k 的倍数

思路:

为了让花费最小,应该选择一个 可以删除 xk,且 k 最小

具体实现如下:

i = 1 开始枚举,不断往后删除 i 的倍数 j,如果 不能删除(即 子集 T 包含了 js[j] == '1'),就 直接 break

用一个 数组 cost 记录一下 每个数字的花费,如果这个数字 在子集 T 中存在枚举 i 的倍数 j,如果 倍数 j 需要删除s[j] == '0'),则 记录一下代价 cost[j]

注意:由于 cost 要取 min,所以 如果 cost[j]0 表示:j 这个数没有删过,就直接 cost[j] = i 即可,如果 不为 0,表示:j 被之前的某个数删除过,由于我们需要找到 最小 k,且 i 从小到大枚举,因此 只需要在 cost 第一次为 0 的时候记录一下即可

最后 将所有 cost 相加 即为答案。

时间复杂度:

标准的 调和级数 O ( n l o g n ) O(nlogn) O(nlogn)

代码:

#include <bits/stdc++.h>

using namespace std;
//#define map unordered_map
#define int long long
const int N = 1e6 + 10;
int n;
string s;
int cost[N];

void solve()
{
	cin >> n;
	cin >> s; s = " " + s;
	fill(cost, cost + n + 1, 0);
	for (int i = 1; i <= n; ++i)
	{
		if(s[i] == '0')
		{
			for (int j = i; j <= n; j += i)
			{
				if(s[j] == '1') break; 
				if (!cost[j]) {
					cost[j] = i;
				}
			}
		}
	}
	int ans = 0;
	for(int i = 1; i <= n; ++i) if(s[i] == '0') ans += cost[i];
	cout << ans << '\n';
}

signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(nullptr), cout.tie(nullptr);

	int _ = 1; cin >> _;

	while (_--)
	{
		solve();
	}

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值