2019牛客暑期多校训练营(第四场)D.triples I(位运算)

题意

给你一个 a a a让你求一个最小的集合 S S S S S S中所有的数都是 3 3 3的倍数,且 S S S中所有的数按位或的和为 a a a,输出 S S S中的元素的个数以及元素

思路

显然 a % 3 = = 0 a\%3==0 a%3==0时,集合 S S S就只含一个 a a a。根据按位或的性质,将 a a a转化为二进制之后, S S S中的元素只会是 a a a的二进制中权值为一的组合。由于二进制的位权模 3 3 3只可能为 1 1 1或为 2 2 2,即
2 0 = 1 (   m o d   3 ) 2^0=1(\ mod\ 3) 20=1( mod 3)
2 1 = 2 (   m o d   3 ) 2^1=2(\ mod\ 3) 21=2( mod 3)
2 2 = 1 (   m o d   3 ) 2^2=1(\ mod\ 3) 22=1( mod 3)
⋮ \vdots
2 n = n % 2 + 1 (   m o d   3 ) 2^n=n\%2+1(\ mod\ 3) 2n=n%2+1( mod 3)
已知 a a a化为二进制,可以表示为 a = 2 p 1 + 2 p 2 + 2 p 3 + ⋯ + 2 p k a=2^{p_1}+2^{p_2}+2^{p_3}+\cdots+2^{p_k} a=2p1+2p2+2p3++2pk
然后考虑 a % 3 = = 1 a\%3==1 a%3==1的情况,由于有 a = 2 p 1 + 2 p 2 + 2 p 3 + ⋯ + 2 p k a=2^{p_1}+2^{p_2}+2^{p_3}+\cdots+2^{p_k} a=2p1+2p2+2p3++2pk,故
2 p 1 + 2 p 2 + 2 p 3 + ⋯ + 2 p k ≡ 1 (   m o d   3 ) 2^{p_1}+2^{p_2}+2^{p_3}+\cdots+2^{p_k}\equiv 1(\ mod\ 3) 2p1+2p2+2p3++2pk1( mod 3)
由于 2 n 2^n 2n只会是 1 1 1或者 2 2 2,那么根据同余式的两边可以可以加减相同的数,那么考虑,若 a a a的二进制中找一个 2 x % 3 = 1 2^x \% 3=1 2x%3=1 2 x 1 % 3 = 2 2^{x_{1}} \% 3=2 2x1%3=2 2 x 2 % 3 = 2 2^{x_{2}} \% 3=2 2x2%3=2( 2 x 1 + 2 x 2 % 3 = 1 2^{x_{1}}+2^{x_{2}}\%3=1 2x1+2x2%3=1)那么令 x = 2 x x=2^x x=2x,那么 a − x a-x ax肯定为3的倍数,那我们再找一个 2 y % 3 = 2 2^y\%3=2 2y%3=2或者找两个 2 y % 3 = 1 2^{y}\%3=1 2y%3=1的,令 y = 2 y y=2^y y=2y,那么 x + y ≡ 0 (   m o d   3 ) x+y\equiv 0(\ mod \ 3) x+y0( mod 3),那么答案就是 a − x a-x ax x + y x+y x+y了,实际上 S S S最小为2就可以满足了,由于题目输入的 a a a有解所有肯定能找到这样的 x x x y y y
a % 3 = = 2 a\%3==2 a%3==2同理

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define pb push_back
vector<int>ans;
 
void solve(int n){
  int res;
  vector<int>yu1,yu2;
  if(n%3==0){
    ans.pb(1);
    ans.pb(n);
    return;
  }
  for(int j=0;j<62;++j){
    if(n>>j&1){
      if((1LL<<j)%3==1){
        yu1.pb(1LL<<j);
      }
      else{
        yu2.pb(1LL<<j);
      }
    }
  }
  ans.pb(2);
 
  if(n%3==1){
    if(yu1.size()){
      int res=yu1.back();
      yu1.pop_back();
      ans.pb(n-res);
      //找剩下的能加出来余2的
      if(yu1.size()>=2){
        res+=yu1.back();
        yu1.pop_back();
        res+=yu1.back();
        yu1.pop_back();
      }
      else{//
        res+=yu2.back();
 
      }
      ans.pb(res);
      //end
      return;
    }
    else{
      res=yu2.back();
      yu2.pop_back();
      res+=yu2.back();
      yu2.pop_back();
      ans.pb(n-res);
      res+=yu2.back();
      ans.pb(res);
    }
    return;
  }
 
 
  if(n%3==2){
    if(yu2.size()){
      res=yu2.back();
      yu2.pop_back();
    }
    else if(yu1.size()>=2){
      res=yu1.back();
      yu1.pop_back();
      res+=yu1.back();
      yu1.pop_back();
    }
    ans.pb(n-res);
    //找剩下加出来能余1的
    if(yu1.size()){
      res+=yu1.back();
    }
    else{
      res+=yu2.back();
      yu2.pop_back();
      res+=yu2.back();
    }
    ans.pb(res);
    return;
  }
}
signed main(){
  ios::sync_with_stdio(false),cin.tie(0);
  int T;
  while(cin>>T){
    while(T--){
      ans.clear();
      int n;cin>>n;
      solve(n);
      for(auto tar:ans) cout<<tar<<' ';
      cout<<endl;
    }
  }
 
  return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值