codeforces 题目 Hamon Odyssey

目录

题目:

题目描述: 

思路:

AC代码:


题目:

题目描述: 

给你长度为 n 的数组,将数组分割成若干连续段(每段里面至少有一个数),对每段内的所有数进行位与运算,再对所有段求完与运算的值进行求和,你需要求出在取得的和的为最小值情况下的最大分段数。

 

为了简化描述,我把上面的一组的 f( l ,r )的值称为组内与值

对于所有组的组内与值求和我将其称为所有组内与值和

思路:

首先我们进行分析,对于与运算,如果我们把整个数组为一组,那么所有组内与值的和一定是最小的值,但组数很有可能不是最小。、

如果要增加组数,那么切割操作相当于把 (a_1 ...a_n) 变为了 (a_1...a_k) , (a_{k+1}...a_n) 这两组。

我们不妨先设 (a_1 ...a_n) 的组内与值为 x ,此时所有组内与值和为 x 。经过拆分,因为我们比原组少了很多与运算,所以 (a_1...a_k) 的组内与值必定大于等于 x , (a_{k+1}...a_n) 的组内与值也必定大于等于 x ,所以此时所有组内与值和大于等于 2x

如果我们要增加组数,但又不影响所有组内与值和最小,会是什么情况?

① 当 x > 0

根据上面分析的情况,必定有2x > x ,所以此时所有组内与值和比刚才大,不可以这么操作。

 当 x == 0

如果想让如果想让所有组内与值和不变,当且仅当 (a_1...a_k) 和 (a_{k+1}...a_n) 的组内与值都为 0

同样的,再次分割也是同样的道理


所以,我们的目标就变成了,看从头开始,我们能最多找出多少组组内与值恰好为 0 的组。那么问题就变成了简单的贪心问题。

找完了前面组内与值恰好为 0 的组,后面剩下的数也不用担心,随便塞到前面的组中就行,他们不影响( 0 & 任何数 == 0 ),此时答案就是前面组内与值恰好为 0 的组的个数

特别的,如果一个都没有找到( 即整个数组为一组,组内与值仍为 0 )那么我们只能将整个数组打包为一组,此时分组个数为 1

思路有了,具体操作请看AC代码:


AC代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N = 2e5 + 5;
const int inf = 2e9 + 5;

int a[N];

void solve()
{
	int cnt = 0;
	ll sum = 0;

	int n;
	cin >> n;
	for (int i = 1; i <= n; i++)
		cin >> a[i];
	
	for (int i = 1; i <= n; i++)
	{
		if (sum == 0)//此时组内与值恰好为0
		{
			sum = a[i];
			cnt++;
			continue;
		}
		sum &= a[i];
	}
	if (sum == 0)//看最后一位与完是否能凑成最小与值为0的组
		cnt++;
	cout << (cnt == 1 ? cnt : cnt - 1) << '\n';
}

int main()
{
	std::ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	int t; cin >> t;
	while (t--)
		solve();
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值