1. 程式人生 > >[Luogu P4317] [BZOJ 3209] 花神的數論題

[Luogu P4317] [BZOJ 3209] 花神的數論題

洛谷傳送門

題目背景

眾所周知,花神多年來憑藉無邊的神力狂虐各大 OJ、OI、CF、TC …… 當然也包括 CH 啦。

題目描述

話說花神這天又來講課了。課後照例有超級難的神題啦…… 我等蒟蒻又遭殃了。 花神的題目是這樣的:設 sum(i)\text{sum}(i)表示 ii 的二進位制表示中 11 的個數。給出一個正整數 NN ,花神要問你 i=1Nsum(i)\prod_{i=1}^{N}\text{sum}(i),也就是 sum(1)sum(N)\text{sum}(1)\sim\text{sum}(N) 的乘積。

輸入輸出格式

輸入格式:

一個正整數 NN

輸出格式:

一個數,答案模 1000000710000007 的值。

輸入輸出樣例

輸入樣例#1:

3

輸出樣例#1:

2

說明

對於 100%100\% 的資料,N1015N≤10^{15}

解題分析

數位dpdp。 考慮形如1111(2)1111_{(2)}這樣的數, 其貢獻為(4(44))×(3(43))×(2(42))×(1(41))(4^\binom{4}{4})\times (3^ \binom{4}{3})\times(2^ \binom{4}{2})\times (1^ \binom{4}{1})

34))×(2(24))×(1(14))

於是我們列舉11總共出現的次數, 從高往低位考慮。 例如11011(2)11011_{(2)}, 我們先考慮000000999900000\sim09999中的貢獻, 然後所需的次數減11, 考慮100001099910000\sim10999的貢獻。 這個過程和SCOI windy數的過程差不多, 都是最後少考慮了11的貢獻, 於是先把N+=1N+=1即可。

組合數預處理的時候對ϕ(MOD)\phi(MOD)取模, 因為這個WA了三次QAQ…

程式碼如下:

#include <cstdio>
#include <cstring>
#include <cctype> #include <algorithm> #include <cmath> #include <cstdlib> #define R register #define IN inline #define W while #define gc getchar() #define MX 65 #define ll long long #define MOD 10000007 #define PHI 9988440 template <class T> IN void in(T &x) { x = 0; R char c = gc; for (; !isdigit(c); c = gc); for (; isdigit(c); c = gc) x = (x << 1) + (x << 3) + c - 48; } IN int fpow(R int base, R int tim) { int ret = 1; W (tim) { if (tim & 1) ret = 1ll * ret * base % MOD; base = 1ll * base * base % MOD, tim >>= 1; } return ret; } int C[MX][MX], dat[MX]; void pre() { for (R int i = 0; i < MX; ++i) C[i][0] = 1; for (R int i = 1; i < MX; ++i) for (R int j = 1; j <= i; ++j) C[i][j] = (C[i - 1][j - 1] + C[i - 1][j]) % PHI; } int len, ans = 1; ll n; IN int solve(R int need) { int ret = 0; for (R int i = len; i; --i) { if(dat[i]) (ret += C[i - 1][need--]) %= PHI; if(need < 0) break; } return ret; } int main(void) { pre(); scanf("%lld", &n); ++n; W (n) dat[++len] = n & 1, n >>= 1; for (R int i = 1; i <= len; ++i) ans = 1ll * ans * fpow(i, solve(i)) % MOD; printf("%d", ans); }