题意大概就是给你一个数,找到它可以由哪些3的倍数的数经过或运算得到,打印最少的个数好相应的解
首先要进行或运算,当然是把数字改写成二进制找规律,然后发现:
1、对于本身就是3的倍数的数,直接输出本身即可,不必考虑;
2、对于只有一位或者两位的值为一的数,无法用3的倍数来拼,不必考虑;
3、若把最低位当做第零位,则对于位数为奇数的1,即2^k%3恒等于2(k=2*n+1,n属于z),对于位数为偶数的1,即2^k%3恒等于1(k=2*n,n属于z),证明如下:
已知2^0=1,1%3=1,则2^0=3k+1,2^1=3k*2+1*2=6k+2,(6k+2)%3=2;如此反复,可以证明。
这个规律有什么用呢?
当然有用,我们可以把二进制的数当做每个为一的位数相加,例如1010=2^3+2^1,这样可以由以上规律可以得到它是不是3的倍数;
那么我们就可以用贪心的思想,对这个数的二进制数,我们找到他第一个为奇数的位数和第一个为偶数的位数,这样两个位数相加一定为3的倍数(对于只有奇数或者只有偶数的参加方法2)
那么我们就可以用剩下的1来拼凑出另外一个数,如果另外一个数不是三的倍数,我们可以通过借第一个数的奇1或者偶1来达成目的(反正或运算两个都是1或了还是1);
2、特殊情况:对于没有奇数或者偶数1的二进制数我们可以考虑用三个奇数1或者偶数1来拼凑出3 的倍数,然后仍旧是借1来使第二个数拼凑成3 的倍数。
下面上代码:
#include<iostream> #include<cstdio> #include<cmath> #include<algorithm> #include<cstring> #include<string> #include<queue> #include<map> #include<set> #include<stack> #include<bitset> #include<vector> using namespace std; #define ll long long #define maxn 200005 ll fen(ll k){ ll s=1,x=2; if(k==0) return 1ll; while(k>1){ if(k%2) s*=x; x*=x; k/=2; } return s*x; } ll n,m,k,t; ll num1,num2,sum; bool a[100]; int main(){ scanf("%lld",&t); while(t--){ scanf("%lld",&k); if(k%3==0||k==1){ printf("1 %lld\n",k); continue; } m=k; ll z=-1; num1=num2=-1; sum=0; while(k){ z++; if(z%2==0&&k%2==1&&num1==-1) num1=z; if(z%2==1&&k%2==1&&num2==-1) num2=z; if(num1!=-1&&num2!=-1) break; a[z]=k&1; k/=2; sum+=a[z]; } if(num1==-1||num2==-1){ int z1=-1,z2=-1,z3=-1; for(int i=0;i<=z;i++){ if(a[i]==1){ if(z1==-1) z1=i; else if(z2==-1) z2=i; else if(z3==-1){ z3=i; break; } } } ll ans1=fen(z1)+fen(z2)+fen(z3); ll ans2=m-ans1; if(sum%3==1) ans2+=(fen(z1)+fen(z2)); else ans2+=fen(z1); printf("2 %lld %lld\n",ans1,ans2); continue; } ll ans1=fen(num1)+fen(num2); ll ans2=m-ans1; if(m%3==1) ans2+=fen(num2); else ans2+=fen(num1); printf("2 %lld %lld\n",ans1,ans2); } }