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

  • 题意:t个测试样例,每次输入一个数n,求n最少能由几个3的倍数的数相或得到

  • 思路:首先,如果n可以被3整除,直接输出n即可。
    如果n不能被3整除,将n写成2进制,如果某一位上为1,则说明他对n具有贡献度,每一位上对3取余不是1就是2,这些余数加起来等于n对3取余所得的余数。现在就是要在这些1中取一些,使取的这些1对3取余所得的余数和为0,且两次取完后包含所有的1。可以考虑贪心的思想,注意分情况。
    如果n%3=1,即需要在n的基础上少取1+s*3就行,如果对3取余等于1的位大于等于2,就可以通过不取1个这样的1来构造;如果对3取余等于1的位等于1 ,则可以一个不取这样的数,另一个通过不取两个余2的位来构造;如果没有对3取余为1的位,则构造的两个数都要通过选取不取两个余2的位来构造。
    如果n%3=2,同理
    举个栗子:n=29;
    对应的二进制为:1 1 1 0 1
    每一位对应的数为:16 8 4 0 1
    每一位对3取余余数为:1 2 1 0 1
    n%3=2
    余2的数只有1个,那么一个数这么构造1 0 1 0 1,等于21
    另一个数为1 1 0 0 0,等于24

  • 完整代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
int a[100005];
ll two[100005]; 
int dis[100005];
vector<int> v[10];
int main(){
	int t;
	cin>>t;
	two[0]=1;
	dis[0]=1;
	for(int i=1;i<1000;i++){//预处理 
		two[i]=two[i-1]*2;
		dis[i]=two[i]%3;
	}
    ll n,m;
   // m=2;

	while(t--){
		
		//m++;
		cin>>m;
		n=m;
		int i=0,l;
		for(i=0;i<4;i++){
			v[i].clear();
		}
        i=0;
		while(n>0){
			a[i]=n%2;
			n=n/2;
			i++;
		} 
		l=i;

		for(i=0;i<l;i++){
			if(a[i]==1&&dis[i]==1){
				v[1].push_back(i);
			}
			else if(a[i]==1&&dis[i]==2){
				v[2].push_back(i);
			}
		}
		if(m%3==0){
			printf("1 %lld\n",m);
		}
		else if(m%3==1){
			ll ans=0,ans1=0;
			if(v[1].size()>1){
				for(i=0;i<(int)(v[1].size())-1;i++){
					ans=ans+two[v[1][i]];
				}
				for(i=1;i<(int)(v[1].size());i++){
					ans1=ans1+two[v[1][i]];
				}
				for(i=0;i<v[2].size();i++){
					ans=ans+two[v[2][i]];
					ans1=ans1+two[v[2][i]];
				}
			}
			else if(v[1].size()==1){
				for(i=0;i<v[2].size();i++){
					ans=ans+two[v[2][i]];
				}
				for(i=0;i<v[1].size();i++){
					ans1=ans1+two[v[1][i]];
				}
				for(i=0;i<(int)(v[2].size())-2;i++){
					ans1=ans1+two[v[2][i]];
				}
			}
			else {
				for(i=0;i<(int)(v[2].size())-2;i++){
					ans1=ans1+two[v[2][i]];
				}
				for(i=2;i<v[2].size();i++){
					ans=ans+two[v[2][i]];
				}
			}
			printf("2 %lld %lld\n",ans,ans1);
		}
		else if(m%3==2){
			ll ans=0,ans1=0;
			if(v[2].size()>=2){
				for(i=0;i<v[1].size();i++){
					ans=ans+two[v[1][i]];
					ans1=ans1+two[v[1][i]];
				}
				for(i=0;i<(int)(v[2].size())-1;i++){
					ans1=ans1+two[v[2][i]];
				}
				for(i=1;i<v[2].size();i++){
					ans=ans+two[v[2][i]];
				}
			} 
			else if(v[2].size()==0){
				for(i=0;i<(int)(v[1].size())-2;i++){
					ans=ans+two[v[1][i]];
				}
				for(i=2;i<(int)(v[1].size());i++){
					ans1=ans1+two[v[1][i]];
				}
			}
			else if(v[2].size()==1){
				for(i=0;i<v[2].size();i++){
					ans=ans+two[v[2][i]];
				}
				for(i=2;i<(int)(v[1].size());i++){
					ans=ans+two[v[1][i]];
				}
				for(i=0;i<v[1].size();i++){
					ans1=ans1+two[v[1][i]];
				}
			}
			
			printf("2 %lld %lld\n",ans,ans1);
		}
		
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值