题意
给定一个数,问是否能将成拆分为不同的阶乘及 2 2 2 的幂次方之和,且最少需要几个这样的数字。
根据二进制的特点,容易得到本题一定有解,且所需数字最多的方案即为二进制中 1 1 1 的个数。因此简化答案需要用阶乘替换若干个 2 2 2 的幂次方。
由于本题的数据范围为 1 0 12 10^{12} 1012 ,因此能取到的最高阶乘为 14 14 14 ,可以对是否取某个阶乘的状态进行枚举,并用 _ _ b u i l t i n _ p o p c o u n t ( ) \_\_builtin\_popcount() __builtin_popcount() 函数求得去除该所选阶乘后剩下的二进制中 1 1 1 的个数。则该状态的答案即为最后 1 1 1 的个数 + 所选阶乘的数量。
大坑点:该函数最大可接受的传入为 i n t int int,调试 2 2 2 的幂次方时发现传入 4294967296 4294967296 4294967296 就寄了,因此要使用 _ _ b u i l t i n _ p o p c o u n t l l ( ) \_\_builtin\_popcountll() __builtin_popcountll() 处理。
#include <bits/stdc++.h>
#define int long long
const int N=1e5+10;
using namespace std;
int p[15];
void deal(){
p[1]=p[0]=1;
for(int i=2;i<=14;i++)
p[i]=i*p[i-1];
}
void solve(){
int n;
cin>>n;
int ans=N;
for(int state=0;state<(1<<14);state++){
int cnt=0,now=n;
for(int i=0;i<14;i++){//对于每个数的阶乘
if((state>>(i))&1){//如果state该位为1 说明选择
if(now<p[i+1]){//当前数小于该阶乘 没有意义
continue;
}
else{
cnt++;
now-=p[i+1];
}
}
}
int sum=__builtin_popcountll(now);
sum+=cnt;//选择了几次阶乘
ans=min(ans,sum);
}
cout<<ans<<endl;
}
signed main(){
int t;
deal();
cin>>t;
while(t--)
solve();
return 0;
}