链接:ac.nowcoder.com/acm/contest...
来源:牛客网
一、分步拆解完整解题思路
步骤 1:分桶 ------ 按二进制位归类下标
核心操作代码(22~26 行)
ini
for(int i=1;i<=n;i++){
bitset<32> bt(a[i]);
for(int j=0;j<=30;j++){
if(bt[j]) v[j].push_back(i);
}
}
逻辑:一个数字二进制有多位 1,它的下标会被放进对应所有位的桶里 。例:a[3]=5 二进制 101,第 0、2 位是 1 → v[0].push_back(3)、v[2].push_back(3)。
步骤 2:无解判定(29~32 行)
cpp
运行
matlab
if(v[i].size()%2){
cout<<"-1";
return;
}
题意约束:每一位上 1 的数量必须偶数,才能全部两两配对;奇数个无法配对,直接输出 - 1。
步骤 3:单个桶最小代价计算 ------get_min_step 核心逻辑
假设 vec 里是从小到大排好序的下标数组(存入时 i 从 1 到 n 遍历,天然有序),长度siz一定是偶数。两种配对方案:
方案 1 ans1:相邻顺次配对
配对组合:(vec[0],vec[1])、(vec[2],vec[3])、(vec[4],vec[5])...总代价 = 每一对右边减左边相加
cpp
运行
ini
for(int i=0;i+1<siz;i+=2)
ans1 += vec[i+1]-vec[i];
方案 2 ans2:错开配对(环形首尾相连)
配对组合:(vec[1],vec[2])、(vec[3],vec[4])...,最后单独一对 (vec.back(), vec[0]),环形距离 (n - vec.back()) + vec[0]
cpp
运行
ini
for(int i=1;i+1<siz;i+=2)
ans2 += vec[i+1]-vec[i];
if(siz!=0) ans2 += (n - vec[siz-1]) + vec[0];
最终取 min (ans1,ans2) 作为当前二进制位的最小代价
步骤 4:全局答案汇总
遍历 0~30 所有二进制位,把每一位算出的最小 temp 累加进 ans,最后输出 ans。
二、你不清楚的核心知识点
知识点 1:vector<int> v[32]; 的本质
- 不是标准二维 vector
vector<vector<int>>; - 是长度固定 32 的静态数组 ,数组每个元素是独立
vector<int>; - 优点:直接按二进制位下标
v[j]访问对应桶,写法简洁; - 限制:行数固定 32 不能动态增减,不能整体拷贝赋值;
- 访问语法:支持
v[i][j],等价(v[i])[j],先取第 i 个 vector,再取内部第 j 个下标。
知识点 2:vector 两种下标访问区别
v[i][j]:越界无报错,静默未定义行为;v[i].at(j):越界抛出异常,调试更安全。
知识点 3:bitset 拆分二进制位
bitset<32> bt(a[i]) 快速取出数字每一位,bt[j] 直接判断第 j 位是否为 1,比位运算x & (1<<j)写法更简洁。
知识点 4:环形数组配对的两种最优模型
偶数个有序点环形配对只有两种最小配对方式:相邻配对 / 错开首尾配对,只需要算两者最小值,不用暴力枚举所有配对(暴力会超时)。
知识点 5:全局数组栈 / 堆分配区分
int a[500005]开在全局区,不会栈溢出;如果写在函数内会爆栈;vector<int> v[32]数组本体在全局区,每个 vector 的数据存储在堆内存。
三、代码隐藏坑点(容易出错的地方)
#define int long long宏定义替换所有 int 为 long long,防止差值累加溢出;- 输入输出加速
ios::sync_with_stdio(false);cin.tie(0),大数据量下避免 TLE; - 下标从 1 开始,环形距离计算要用
n - vec.back() + vec[0],不能直接vec[0] - vec.back()(会出现负数); - 桶为空
siz=0时,ans2 不需要额外加环形代价,代码做了siz!=0判断规避。
四、整体解题思想总结
- 分治思想:二进制位独立处理每一位之间互不影响,分开计算代价再求和;
- 贪心思想:有序偶数点环形配对仅两种最优方案,不用 DP / 暴力,线性遍历即可算出最小值;
- 分桶思想:静态数组套 vector,快速按二进制位归类下标,实现 O (n*32) 线性复杂度。
五、代码实现
ini
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n;
int a[500005];
vector<int> v[32];
int get_min_step(vector<int> vec){
int siz=vec.size();
int ans1=0,ans2=0;
for(int i=0;i+1<siz;i+=2){
ans1+=(vec[i+1]-vec[i]);
}
for(int i=1;i+1<siz;i+=2){
ans2+=(vec[i+1]-vec[i]);
}
if(siz!=0) ans2+=(n-vec[siz-1])+(vec[0]);
return min(ans1,ans2);
}
void solve(){
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
bitset<32> bt(a[i]);
for(int j=0;j<=30;j++){
if(bt[j]) v[j].push_back(i);
}
}
int ans=0;
for(int i=0;i<=30;i++){
if(v[i].size()%2){
cout<<"-1";
return;
}
int temp=get_min_step(v[i]);
ans+=temp;
}
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
solve();
}