1. 程式人生 > 其它 >C. Factorials and Powers of Two(列舉,暴力)

C. Factorials and Powers of Two(列舉,暴力)

C. Factorials and Powers of Two

Tag

列舉 暴力

題目來源

Codeforces Round #774 (Div. 2)

題目大意

  • 求問一個數能拆成最少多少個powerful的數字的和,powerful的數的定義為這個數要麼是2的次方,要麼是某個數的階乘,如果沒有方案,則輸出-1

解題思路

  • 我們知道一個數一定能夠通過2的n次方的和加起來,比如6(110),\(6 = 2^1 + 2^2\),因此不存在輸出-1的情況
  • 現在我們需要知道能否通過若干個階乘的結果來得到最優的答案,我們知道,\(15! \gt 10^{12}\),因此我們只需要不超過15的階乘的值
  • 那麼需要多少個階乘呢?我們可以通過二進位制進行列舉,複雜度為\(2^{15}\)
    ,表示我們需要某個數的階乘,例如(11100)表示我們同時獲得階乘\(fac[2], fac[3], fac[4]\)的值
  • 原先的數減去這些個階乘的數字之後,剩下的就只需要看剩下的數二進位制中有多少個1了

AC Code

#include <bits/stdc++.h>
using namespace std;
#define IOS ios::sync_with_stdio(0)
#define LL long long
#define maxn (int)(2e6 + 10)
#define FFF freopen("out", "w", stdout);

LL n;
int a[maxn];
int ans[maxn];

LL fac[20];
void init()
{
    fac[0] = 1;
    for ( int i = 1 ; i <= 15 ; i++ )
        fac[i] = fac[i-1] * i;
    return ;
}

int check(LL x )
{
    int cnt = 0 ;
    while ( x)
    {
        if( x & 1 )
            cnt++; 
        x >>= 1;
    }
    return cnt;
}

int main ()
{
    IOS;
    init();
    int T ; cin >> T;
    for ( int cas = 1 ; cas <= T ; cas++ )
    {
        cin >> n;
        int minx = check(n);
        for ( int i = 8 ; i <= 1 << 15 ; i+=8 )
        {
            LL tmp = n ;
            int cnt = 0 ;
            for ( int j = 0 ; j <= 15 ; j++ )
                if ( (1 << j) & i ) 
                {
                    cnt++;
                    tmp -= fac[j];
                }
            if ( tmp < 0 ) break;
            minx = min(minx, cnt + check(tmp));
        }
        ans[cas] = minx;
    }
    for ( int i = 1 ; i <= T ; i++ )
        cout << ans[i] << endl;
}