目录
题目:
题目描述:
给你长度为 n 的数组,将数组分割成若干连续段(每段里面至少有一个数),对每段内的所有数进行位与运算,再对所有段求完与运算的值进行求和,你需要求出在取得的和的为最小值情况下的最大分段数。
为了简化描述,我把上面的一组的 f( l ,r )的值称为组内与值
对于所有组的组内与值求和我将其称为所有组内与值和
思路:
首先我们进行分析,对于与运算,如果我们把整个数组为一组,那么所有组内与值的和一定是最小的值,但组数很有可能不是最小。、
如果要增加组数,那么切割操作相当于把 变为了 , 这两组。
我们不妨先设 的组内与值为 x ,此时所有组内与值和为 x 。经过拆分,因为我们比原组少了很多与运算,所以 的组内与值必定大于等于 x , 的组内与值也必定大于等于 x ,所以此时所有组内与值和大于等于 2x
如果我们要增加组数,但又不影响所有组内与值和最小,会是什么情况?
① 当 x > 0
根据上面分析的情况,必定有2x > x ,所以此时所有组内与值和比刚才大,不可以这么操作。
② 当 x == 0
如果想让如果想让所有组内与值和不变,当且仅当 和 的组内与值都为 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;
}