题目描述
Doctor Elephant is testing his new program: output the bitwise or of the numbers inputed.He has decided to input several multiples of 3 and the output of the program should be his favorite number a a a.Because he is lazy, he decided to input as few numbers as possible. He wants you to construct such an input for every a a a he chose. It’s guaranteed that for every aa in the input there is such a solution. If there’re multiple solutions you can output any.
输入描述:
There’re multiple test cases in a test file.The first line contains a positive integer T T T - the number of test cases. In each of the following T lines there is one positive integer a a a.
输出描述:
For each test case output a line. First you need to output the number of numbers in your input, and then you need to output these numbers, separating by spaces.
示例1
输入
2 3 7
输出
1 3 2 3 6
说明
3=3, (3|6)=7
备注:
1 ≤ T ≤ 1 0 5 , 1 ≤ a ≤ 1 0 18 1 \leq T \leq 10^5, 1≤a≤10^{18} 1≤T≤105,1≤a≤1018
题目给出T组样例,每组包含一个数,要求分解为一些3的倍数的数字或操作的和,而其中这些数尽可能少,输出这些数(保证有解,解不一定唯一)。
重要结论:
首先我们要发现一个规律,二进制数的奇数位对应的数模3余2,偶数位模3余1(包括0号位)。
不妨设奇数位的权值为2,偶数位权值为1,那我们就只需要凑奇偶位的带权和为3的倍数,即可满足为3的倍数。
解题思路:
首先发现结论就直接开始是不行的,因为要求的分解得数要最少,那么我们就可以在观察中发现一些东西:所有可以分解的数字都可以在两个数以内被分解。
证明:
1)当给出的数原本为3的倍数时,就是它本身就可以解决问题。
2)当给出的数不是为3的倍数时,那么它的带权总和模3为1或者2。而且这个问题能被解决,那它最少可以分解出一个三的倍数,那么剩余的权值的和模3就与带权总和模3相等,为1或者2。
回头看那个已经确定的三的倍数的情况有3种:
(1)只含有1,那么最少3个1,此时无论剩余权的和模3为1还是2,都可以从中取1个或者2个1解决了。
(2)只含有2,那么最少3个2此时无论剩余权的和模3为1还是2,都可以从中取1个或者2个2解决了。
(3)都含有,最少一个1一个2,此时无论剩余权的和模3为1还是2,都可以从中取1个1或者2解决了。
那么到此为止就证明了所有可以分解的数字都可以在两个数以内被分解。
那么问题就只剩代码了,这些情况实现的时候怎么具体操作呢?
其实只要注意当权值全为1,全为2,都含有,三种情况就可以当做2)中(1)(2),(3)对于去写就行了。
由于本人开始理解错了题意,明白了之后懒得自己敲了,就整人家代码吧。
当时后来也不想自己敲了,那就借用牛客大神的代码吧(目前又找不到出处,如果您发现是您的代码请尽快联系我,请不要反手直接举报,谢谢)
牛客大佬代码:
#include<cstring>
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
ll num1[120],num2[120];
int cnt1,cnt2;
int main()
{
int t;
cin>>t;
while(t--)
{
ll n;
cin>>n;
cnt1=0,cnt2=0;
ll ans1=0,ans2=0;
memset(num1,0,sizeof(num1));
memset(num2,0,sizeof(num2));
if(n%3==0)
{
printf("1 %lld\n",n);
continue;
}
for(int i=0;(1ll<<i)<=n;i++)
{
if(n&(1ll<<i))
{
if((1ll<<i)%3==1)
num1[++cnt1]=1ll<<i;
else if((1ll<<i)%3==2)
{
num2[++cnt2]=1ll<<i;
}
}
}
if(cnt1>0&&cnt2>0)
{
ans1=num1[1]+num2[1];
for(int i=2;i<=cnt1;i++)
{
ans2+=num1[i];
}
for(int i=2;i<=cnt2;i++)
{
ans2+=num2[i];
}
if(ans2%3==1)
ans2+=num2[1];
if(ans2%3==2)
ans2+=num1[1];
}
else if(cnt1>0)
{
ans1=num1[1]+num1[2]+num1[3];
for(int i=4;i<=cnt1;i++)
{
ans2+=num1[i];
}
if(ans2%3==1)
{
ans2+=num1[1]+num1[2];
}
if(ans2%3==2)
{
ans2+=num1[1];
}
}
else if(cnt2>0)
{
ans1=num2[1]+num2[2]+num2[3];
for(int i=4;i<=cnt2;i++)
{
ans2+=num2[i];
}
if(ans2%3==1)
ans2+=num2[1];
if(ans2%3==2)
ans2+=num2[1]+num2[2];
}
cout<<2<<" "<<ans1<<" "<<ans2<<endl;
}
return 0;
}
.