Luogu 4317 花神的数论题

披着数论题外衣的数位dp。

相当于数一数$[1,n]$范围内$1$的个数是$1,2,3,4,...log(n)$的数各有多少个,直接在二进制下数位dp。

然而我比较sb地把(1e7 + 7)当成了质数,其实数出来的数是要模$\phi(p)$的,然而数出来的数绝对不会超过$n$。

时间复杂度$O(log^{4}n + \sqrt{P})$。

Code:

#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;

const int N = 65;
const ll P = 1e7 + 7;

int len, bit[N];
ll f[N][N], phiP;

inline ll pow(ll x, ll y) {
    ll res = 1LL;
    for(; y > 0; y >>= 1) {
        if(y & 1) res = res * x % P;
        x = x * x % P;
    } 
    return res;
}

ll dfs(int pos, int cnt, bool lead, bool lim, int cur) {
    if(pos == 0) return (cnt == cur);
    if(!lead && !lim && f[pos][cnt] != -1) return f[pos][cnt];
    
    ll res = 0LL; int num = lim ? bit[pos] : 1;
    for(int i = 0; i <= num; i++)
        res = (res + dfs(pos - 1, cnt + (i == 1), lead && (i == 0), lim && (i == bit[pos]), cur)) % phiP;
    
    if(!lim && !lead) f[pos][cnt] = res;
    return res;
}

inline ll solve(int k) {
    memset(f, -1, sizeof(f));
    ll res = dfs(len, 0, 1, 1, k);
    return res;
}

inline ll getPhi(ll now) {
    ll res = now, tmp = now; 
    for(int i = 2; i * i <= now; i++) 
        if(tmp % i == 0) {
            res = res / i * (i - 1);
            for(; tmp % i == 0; tmp /= i);
        }
    if(tmp != 1) res = res / tmp * (tmp - 1);
    return res;
}

int main() {
    phiP = getPhi(P);
//    printf("%lld\n", phiP);
    
    ll n; scanf("%lld", &n);
    
    len = 0;
    for(ll tmp = n; tmp > 0; tmp >>= 1)
        bit[++len] = (tmp & 1);
    
    ll ans = 1LL;
    for(int i = 1; i <= len; i++)
        ans = ans * pow(i, solve(i) % phiP) % P;
    
    printf("%lld\n", ans);
    return 0;
}    
View Code

 

posted @ 2018-09-15 08:29  CzxingcHen  阅读(154)  评论(0编辑  收藏  举报