• NOIP模拟赛2(two)


    题目描述 Description###

    很久很久很久以前,方方方造了一台计算机,计算机中开始有一个数 (0) 。方方方想要让这个数变成 (a) ,他打算每次选择一个整数,把计算机中当前的数按位或上这个数。
    这时候黑恶势力 (zzq) 出现了:为了符合题目名称,方方方选择的整数必须是三的倍数。zzq 想知道在这个条件下能不能得到 (a) ,如果能得到,给出一种步数尽量小的方案。

    输入描述 Input Description###

    第一行一个整数 (T) ,表示数据组数。
    接下来 (T) 行每行一个正整数 (a) ,表示想要得到的整数。

    输出描述 Output Description###

    对于每组数据,如果该组数据无解,输出一行(-1)
    如果该组数据有解,你需要在一行中输出方案,行的开头输出你需要进行几
    次或操作,接下来用空格分割输出你要或上的每个数。
    不懂的话可以参见样例输出。

    样例输入 Sample Input###

    3
    3
    2
    7
    

    样例输出 Sample Output###

    1 3
    -1
    2 3 6
    

    数据范围及提示 Data Size & Hint###

    对于 20%的数据,(a,T<=10)
    对于 50%的数据,(a,T<=1000)
    对于 100%的数据,(1<=T<=200000,1<=a<=10^18)

    之前的一些废话###

    题解###

    考虑二进制数与三的倍数的关系。谈到二进制数是由若干个(2^n) 组成,分析其中一个(2^n) ,我们发现如果n是奇数的话,模(3) 等于$2 (,)n$ 为偶数模(3) 等于(1) ,如果若干个(2^n) 组成的话,我们就可以发现:(先定义奇数二进制为n为奇数的(2^n),偶数类似 )三个奇数二进制可以凑成三的倍数,三个偶的也可以凑成散的倍数,一个奇的一个偶的也可以凑成三的倍数。所以思路已经很清晰了:对于每一个输入进来的(n) ,首先分析它有多少个奇数二进制与多少个偶数二进制,然后根据以上配对原则选取不同的(2^n) 来贪心的拼凑三的倍数,可以发现至多只需要两个三的倍数即可凑成。

    代码###

    #include<iostream>
    #include<cmath>
    #include<algorithm>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<queue>
    using namespace std;
    typedef long long LL;
    typedef pair<int,int> PII;
    inline LL read()
    {
    	LL x=0,f=1;char c=getchar();
    	while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}
    	while(isdigit(c)){x=x*10+c-'0';c=getchar();}
    	return x*f;
    }
    const int maxn=100010;
    int T,even,odd,sum;
    LL a,b,c,d,ji[70],ou[70];
    int main()
    {
    	freopen("three.in","r",stdin);
    	freopen("three.out","w",stdout);
    	T=(int)read();
    	while(T--)
    	{
    		a=read();sum=even=odd=0;b=c=0ll;
    		for(LL i=0;(1ll<<i)<=a;i++)
    			if(a&(1ll<<i))
    			{
    				sum++;
    				if(i%2)ji[++odd]=1ll<<i;
    				else ou[++even]=1ll<<i;
    			}
    		if(sum<2 || (odd==0 && even<3) || (even==0 && odd<3)){printf("-1
    ");continue;}
    		if(a%3==0){printf("1 %lld
    ",a);continue;}
    		if(odd && even)
    		{
    			for(int i=1;i<=min(odd,even);i++)b+=(ji[i]+ou[i]);
    			if(even>=3 && even>odd)
    			{
    				for(int i=odd+1;i<=even;i++)c+=(ou[i]);
    				if((even-odd)%3==1 && odd>1)c+=(ou[odd]+ou[odd-1]);
    				else if((even-odd)%3==1 && odd==1)c+=ji[1];
    				if((even-odd)%3==2)c+=ou[odd];
    				printf("2 %lld %lld
    ",b,c);
    				continue;
    			}
    			else if(odd>=3 && odd>even)
    			{
    				for(int i=even+1;i<=odd;i++)c+=(ji[i]);
    				if((odd-even)%3==1 && even>1)c+=(ji[even]+ji[even-1]);
    				else if((odd-even)%3==1 && even==1)c+=ou[1];
    				if((odd-even)%3==2)c+=ji[even];
    				printf("2 %lld %lld
    ",b,c);
    				continue;
    			}
    			else if(odd>even)
    			{
    				for(int i=even+1;i<=odd;i++)c+=(ji[i]+ou[i-even]);
    				printf("2 %lld %lld
    ",b,c);
    			}
    			else if(odd<even)
    			{
    				for(int i=odd+1;i<=even;i++)c+=(ou[i]+ji[i-odd]);
    				printf("2 %lld %lld
    ",b,c);
    			}
    		}
    		else if(even)
    		{
    			for(int i=1;i+3<=even;i+=3)b+=(ou[i]+ou[i+1]+ou[i+2]);
    			c=ou[even]+ou[even-1]+ou[even-2];
    			printf("2 %lld %lld
    ",b,c);
    			continue;
    		}
    		else if(odd)
    		{
    			for(int i=1;i+3<=odd;i+=3)b+=(ji[i]+ji[i+1]+ji[i+2]);
    			c=ji[odd]+ji[odd-1]+ji[odd-2];
    			printf("2 %lld %lld
    ",b,c);
    			continue;
    		}
    	}
    	return 0;
    }
    

    总结###

    这种题重点在于缕清思路,一步步推还是比较好想的。事实证明我实在不适合这种分类讨论情况这么多的题,漏解了也很难发现,十分庆幸这是一道对拍十分好写的题,如果考试时候碰上这种难对拍的话,那就老老实实写50分做法吧。

  • 相关阅读:
    spring-cloud-feign案例
    spring-cloud-hystrix熔断
    科技创业公司最爱的9大工具箱
    近期入手一树莓派卡片机,体验了一下它的强大,写篇报告
    Android应用盈利广告平台的嵌入方法详解
    UnityVS(Visual Studio Tools For Unity)的安装与使用
    unity3d快捷键大全
    Unity3D Android手机开发环境配置,可真机发布调试
    谷歌最近收购的机器人公司
    Microsoft Robotics Developer Studio 4
  • 原文地址:https://www.cnblogs.com/FYH-SSGSS/p/7761963.html
Copyright © 2020-2023  润新知