D-triples
题意
给你一个(n),问至少有几个数或运算起来可以等于(n),并且输出数量和这个几个数。题目说明给的(n)一定符合条件(不会输出(n= 1) 之类不存在情况)。
思路
-
我们打个表就能知道n至少可以由(1)个或者(2)个数或起来。
-
首先我们预先判断(n \% 3 == 0)这种输出(n)自己本身就可以了
-
其它的数可以由(2)个数进行或运算得到。
- 把(n)转换为二进制,把每一位上1提取出来放到集合(R)
- 找到两个集合(a、b),(acup b = R)
(num1 = sum_{i=0}^{a.size()} x_{i} (x_{i}in a))
(num1 \% 3 == 0)
(num2 = sum_{i=0}^{b.size()} y_{i} (y_{i}in b))
(num2 \% 3 == 0) - 再分别把(a、b)集合里的数加起来就是答案了。
样例(解决WA到哭的两个样例)
2
85
682
(1010101、1010101010)上面两个样例的二进制
AC代码
#include<bits/stdc++.h>
#define mes(a, b) memset(a, b, sizeof a)
using namespace std;
typedef long long ll;
int a[100];
int ans[3];
void solve(ll x){ //转换n为二进制
int i = 0;
while(x){
a[i++] = x%2;
x /= 2;
}
}
int main(){
int t;
ll n;
scanf("%lld", &t);
while(t--){
scanf("%lld", &n);
if(n % 3ll == 0){
printf("1 %lld
", n);
continue;
}
mes(a, 0);
solve(n);
ans[1] = ans[2] = 0; //分别计算二进制位为1的数字取模3分别为1和2的数量
for(int i = 0; i <= 64; i++){
if(a[i] == 1){
int num = (1ll<<i)%3ll;
ans[num]++;
}
}
int sum = ans[1] + ans[2]*2;
int b[3][3];
mes(b, 0);
if(sum % 3 ==1){ //强行分组(本人比较菜只会这样写)
if(ans[1] == 0){
b[1][1] = 0;
b[1][2] = ans[2] - 2;
b[2][1] = 0;
b[2][2] = 3;
}
else{
b[1][1] = ans[1]-1;
b[1][2] = ans[2];
if(ans[2] == 0){
b[2][1] = 3;
}
else{
b[2][1] = 1;
b[2][2] = 1;
}
}
}
else if(sum %3 == 2){
if(ans[2] == 0){
b[1][1] = ans[1]-2;
b[1][2] = 0;
b[2][1] = 3;
b[2][2] = 0;
}
else{
b[1][1] = ans[1];
b[1][2] = ans[2]-1;
if(ans[1] == 0){
b[2][1] = 0;
b[2][2] = 3;
}
else{
b[2][1] = 1;
b[2][2] = 1;
}
}
}
ll num1 = 0, num2 = 0;
for(int i = 0; i < 64; i++){
if(a[i] == 1){
int num = (1ll<<i)%3;
if(b[2][num] == ans[num]){
num2 += (1ll<<i);
b[2][num]--;
}
if(b[1][num] > 0){
num1 += (1ll<<i);
b[1][num]--;
}
ans[num]--;
}
}
printf("2 %lld %lld
", num1, num2);
}
return 0;
}