1. 程式人生 > >BZOJ3209 花神的數論題 【組合數 + 按位計數】

BZOJ3209 花神的數論題 【組合數 + 按位計數】

spa using code 需要 cnblogs 來講 fin tdi pac

題目

背景
眾所周知,花神多年來憑借無邊的神力狂虐各大 OJ、OI、CF、TC …… 當然也包括 CH 啦。
描述
話說花神這天又來講課了。課後照例有超級難的神題啦…… 我等蒟蒻又遭殃了。
花神的題目是這樣的
設 sum(i) 表示 i 的二進制表示中 1 的個數。給出一個正整數 N ,花神要問你
派(Sum(i)),也就是 sum(1)—sum(N) 的乘積。

輸入格式

一個正整數 N。

輸出格式

一個數,答案模 10000007 的值。

輸入樣例

3

輸出樣例

2

提示

對於樣例一,112=2;

數據範圍與約定

對於 100% 的數據,N≤10^15

題解

直接求太大,通過手算可以發現,由於乘法的交換律,我們可以把2放在一起,3放在一起,4放在一起........
就有\(ans = 1^{a1} + 2^{a2} + 3^{a3} + ....... + n^{an}\)


所以我們只需要求出包含特定數量1的數有多少個

但有一個上限N的限制,我們考慮遞歸計算
cal(u,v)表示u位【從高到低計算】及其之後放入v個1的合法方案
如果N的u位上是1,說明可以放1
如果放1,那麽往下遞歸cal(u - 1,v - 1)
如果不放,之後\(u - 1\)位無論如何放,都不會大於N,所以就有\(C_{u - 1}^{v}\)中方案

最後累計出每一個指數,統計答案

現在考慮取模
值得一提的是,,\(1000007\)不是質數,它等於\(941 * 10627\),所以我們不能用費馬小定理
而應該用更一般的形式:\(a^{\phi(p)} \equiv 1 (mod p)\),而\(\phi(10000007) = \phi(941) * \phi(10627) = 940 * 10626 = 9988440\)


那麽指數運算時就模9988440就可以了
由於9988440不是質數,可能不存在逆元,組合數用遞推式預處理出

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long int
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define Redge(u) for (int k = h[u]; k; k = ed[k].nxt)
using namespace std;
const
int maxn = 70,maxm = 100005,INF = 1000000000,P = 10000007,M = 9988440; LL C[maxn][maxn],N,bit[maxn],n; void init(){ C[0][0] = 1; for (int i = 1; i < maxn; i++){ C[i][0] = C[i][i] = 1; for (int j = 1; j <= (i >> 1); j++) C[i][j] = C[i][i - j] = (C[i - 1][j - 1] + C[i - 1][j]) % M; } } LL qpow(LL a,LL b){ LL ans = 1; for (; b; b >>= 1,a = a * a % P) if (b & 1) ans = ans * a % P; return ans % P; } LL cal(int u,int v){ if (!v) return 1; if (!u || u < v) return 0; if (!bit[u]) return cal(u - 1,v); return (C[u - 1][v] + cal(u - 1,v - 1)) % M; } int main(){ init(); cin >> N; for (n = 1; N; n++,N >>= 1) bit[n] = N & 1; n--; LL ans = 1; for (LL i = 1; i <= n; i++) ans = ans * qpow(i,cal(n,i)) % P; printf("%lld\n",(ans % P + P) % P); return 0; }

BZOJ3209 花神的數論題 【組合數 + 按位計數】