#P2726. 第2题-二进制数组
-
1000ms
Tried: 77
Accepted: 9
Difficulty: 5
所属公司 :
阿里
时间 :2025年3月22日-阿里淘天(算法岗)
-
算法标签>模拟
第2题-二进制数组
二进制数组
题目描述
给定一个正整数数组构造二进制数 n,计算 (n XOR (n >> 1)) % m。其中二进制数的构造规则为:数组中第 i 个元素表示连续 a_i 个 i%2 的二进制位(从高位到低位排列)。
算法思路
核心观察
- 异或特性:
n XOR (n >> 1)的二进制中,只有相邻位不同的位置会产生1 - 段边界规则:相邻段的奇偶性不同时,交界处会生成1
- 快速幂优化:直接计算大数不可行,需用快速幂逐位处理
关键步骤
- 前缀和预处理:快速定位每个段的边界位置
- 最高位处理:二进制最高位总是由第一个段决定,直接计算贡献
- 段边界筛选:只处理奇偶性变化的段边界
- 模运算优化:使用快速幂算法处理大指数取模
复杂度分析
- 时间复杂度:O(k + k*logL)
- 前缀和计算 O(k)
- 快速幂计算 O(k*logL),L为二进制总位数
- 空间复杂度:O(k) 用于存储前缀和数组
Python 代码
k, m = map(int, input().split()) # 输入k和m
a = list(map(int, input().split())) # 输入数组a
b = [] # 用来存储二进制序列
current_value = a[-1] % 2 # 记录当前段的奇偶性,0表示偶数,1表示奇数
count = 0 # 记录当前段的长度
for i in range(k-1,-1,-1):
if a[i] % 2 == current_value: # 当前元素与上一段的奇偶性相同
count += a[i] # 累加当前段的长度
else:
b.append(count) # 将上一段的长度存入b
current_value = a[i] % 2 # 更新当前段的奇偶性
count = a[i] # 重置当前段的长度为当前元素的值
# 将最后一段的长度也加到b中
if count != 0:
b.append(count)
if a[0] % 2 == 0:
b.pop()
result = 0 # 最终结果
exponent = 0
# 遍历b数组进行计算
for i in range(len(b)):
exponent += b[i]
contribution = pow(2, exponent-1, m) # 计算贡献值,模m
result = (result + contribution) % m # 累加结果
print(result % m) # 输出最终结果
C++
#include <bits/stdc++.h>
using namespace std;
long long fast_pow(long long base, long long exp, long long mod) {
long long res = 1;
base %= mod;
while (exp > 0) {
if (exp & 1) {
res = res * base % mod;
}
base = base * base % mod;
exp >>= 1;
}
return res;
}
int main() {
int k;
long long m;
cin >> k >> m;
vector<int> a(k);
for (int i = 0; i < k; ++i) {
cin >> a[i];
}
vector<long long> b;
int current_value = a[k-1] % 2; // 记录当前段的奇偶性,0表示偶数,1表示奇数
long long count = 0;
for (int i = k-1; i >= 0; --i) {
if (a[i] % 2 == current_value) { // 当前元素与上一段的奇偶性相同
count += a[i];
} else {
b.push_back(count);
current_value = a[i] % 2;
count = a[i];
}
}
// 将最后一段的长度也加到b中
if (count != 0) {
b.push_back(count);
}
// 如果首位是偶数,移除第一个元素
if (a[0] % 2 == 0) {
b.pop_back();
}
long long result = 0;
long long exponent = 0;
// 遍历b数组进行计算
for (int i = 0; i < b.size(); ++i) {
exponent += b[i];
long long contribution = fast_pow(2, exponent - 1, m); // 计算贡献值,模m
result = (result + contribution) % m;
}
cout << result % m << endl;
return 0;
}
Java
import java.util.*;
public class Main {
static long fastPow(long base, long exp, long mod) {
long res = 1;
base %= mod;
while (exp > 0) {
if ((exp & 1) == 1)
res = res * base % mod;
base = base * base % mod;
exp >>= 1;
}
return res;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int k = sc.nextInt();
long m = sc.nextLong();
int[] a = new int[k];
for (int i = 0; i < k; i++) a[i] = sc.nextInt();
List<Long> b = new ArrayList<>();
int currentValue = a[k-1] % 2;
long count = 0;
for (int i = k-1; i >= 0; i--) {
if (a[i] % 2 == currentValue) {
count += a[i];
} else {
b.add(count);
currentValue = a[i] % 2;
count = a[i];
}
}
if (count != 0) {
b.add(count);
}
if (a[0] % 2 == 0) {
b.remove(b.size() - 1);
}
long result = 0;
long exponent = 0;
for (long val : b) {
exponent += val;
long contribution = fastPow(2, exponent - 1, m);
result = (result + contribution) % m;
}
System.out.println(result % m);
}
}
题目内容
对于给定的正偶数n和正整数m,求解下式:
n xor2nmod m
这显然难不倒你,所以,我们将会使用一种特殊的方式给出n的二进制形式:给出一个由k个整数构成的数组{a1,a2,...,ak},其中,第i个整数ai代表n的二进制表示中,从高位到低位,恰好有连续ai个ai mod 2。更具体地,如果数组a={3,4,1,2},那么,第一个整数a1就代表有3 个3 mod 2=1,第二个整数a2就代表有4 个4 mod 2=0,......。最终,可以得到n的二进制表示为1110000100。
输入描述
第一行输入两个整数 k,m(1≦k≦2×105;1≦m≦109)。
第二行输入k个正整数a1,a2,...,ak(1≦ai≦109)。
除此之外,保证单个测试文件的ai之和不超过109。
输出描述
输出一个十进制整数,代表式子的最终答案.
样例1
输入
4 15
3 4 1 2
输出
12