【2015-2016 ACM-ICPC, NEERC, Northern Subregional Contest D】【脑洞 构造】Distribution in Metagonia 把数以2^p3^

48 篇文章 0 订阅
24 篇文章 0 订阅
#include<stdio.h> 
#include<iostream>
#include<string.h>
#include<ctype.h>
#include<math.h>
#include<map>
#include<set>
#include<vector>
#include<queue>
#include<string>
#include<algorithm>
#include<time.h>
#include<bitset>
using namespace std;
#define MS(x,y) memset(x,y,sizeof(x))
#define MC(x,y) memcpy(x,y,sizeof(x))
#define MP(x,y) make_pair(x,y)
#define ls o<<1
#define rs o<<1|1
typedef long long LL;
typedef unsigned long long UL;
typedef unsigned int UI;
template <class T> inline void gmax(T &a,T b){if(b>a)a=b;}
template <class T> inline void gmin(T &a,T b){if(b<a)a=b;}
const int N=1e5+1e4,M=0,Z=1e9+7,maxint=2147483647,ms31=522133279,ms63=1061109567,ms127=2139062143;const double eps=1e-8,PI=acos(-1.0);//.0
void fre()
{
    freopen("distribution.in","r",stdin);
    freopen("distribution.out","w",stdout);
}
int casenum,casei;
LL n,m;
LL a[1000];
void solve(LL n,LL mul)
{
    while(n%2==0)
    {
        n/=2;
        mul*=2;
    }
    while(n%3==0)
    {
        n/=3;
        mul*=3;
    }
    if(n==1)a[++m]=mul;
    else
    {
        LL x;for(x=3;x*3<n;x*=3);
        a[++m]=mul*x;
        solve(n-x,mul);
    }
}
int main()
{
    fre();
    scanf("%d",&casenum);
    for(casei=1;casei<=casenum;casei++)
    {
        scanf("%lld",&n);m=0;
        solve(n,1);
        printf("%lld\n",m);
        for(int i=1;i<=m;i++)printf("%lld ",a[i]);
        puts("");
    }
}
/*
【trick&&吐槽】
还是那句话,构造题先构造数据找规律。

【题意】
T([1,1000]组数据),
对于每组数据,给你一个数n([1,1e18]),
让你把n拆成若干个数(m个数,m是正整数)之和的形式,
使得这m个数都是2^p*3^q这样的形式,因子中只有2和3,而且相互之间不是倍数关系。
并输出。

【类型】
构造

【分析】
遇到构造题,很难有头绪的时候,我们可以首先构造小数据来猜解。
在小数据的构造中,我们感觉,对于所有的数都有构造方案。于是——

操作1,我们发现,如果一开始的n可以表示成n=2^p*3^q*w的形式,
那么我们直接使得n/=(2^p*3^q),然后对于w进行构造即可。
为什么这样子可以呢?因为我们对于w可能还有数的拆分,
而拆分成的若干个数,都含有2^p*3^q。
所以只要w被拆分成的若干个数之间不相互是倍数关系,那最后就不会出现倍数关系

2,然后现在我们要处理w啦。
w中是不含有任何因子2和因子3的,既然如此w就必定为奇数。
如果w为1,那么说明w已经满足目标条件了,我们直接输出即可。
如果w不为1,那么我们执行以下的操作。
找到一个最大的数v,使得v=3^p且v<w。
为什么会产生这种思路呢?很简单,因为我们最简单的拆分方案就是只拆分成2的若干次方或3的若干次方。
而这里拆分出一个3^p,必然使得剩下的数是2的倍数

3,经过之前这两步,我们相当于把初始的n,拆成了mul0*(A+B),其中A是能拆分的最大3的次方,B是偶数。
A必然不是B的倍数,因为A是3的次方,是奇数,而B却是偶数;
同时B必然不是A的倍数,因为如果B是A的倍数,那至少为2倍,那就不符合A是能拆分的最大3的次方这一条件。
然而B不一定恰好是符合要求的拆分数。这意味着B还要继续拆分下去。然而B一定是偶数,至少含有一个因子2,
这样我们先把B中的2和3提取出来是,使得可以写成mul0*(A+mul1*(B+C))之类的形式。
这时,B和C中比A至少多含有一个2,所以A一定不是B和C的倍数,而且A也不是C的继续拆分数的倍数。
同时,因为A是能拆分的最大3的次方的性质,所以B和C以及其继续拆分的数也不会是A的倍数。
重复这个过程下去,因为每次都是拆分成3的最大次方+偶数,所以拆分一定可以最终完成,得到最后的结果。

【时间复杂度&&优化】
O(Tlogn)

*/

  • 5
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值