【洛谷】4317:花神的数论题【数位DP】

P4317 花神的数论题

题目背景

众所周知,花神多年来凭借无边的神力狂虐各大 OJ、OI、CF、TC …… 当然也包括 CH 啦。

题目描述

话说花神这天又来讲课了。课后照例有超级难的神题啦…… 我等蒟蒻又遭殃了。 花神的题目是这样的:设 sum(i)表示 i 的二进制表示中 1 的个数。给出一个正整数 N ,花神要问你 i=1Nsum(i) ,也就是sum(1)sum(N)的乘积。

输入输出格式

输入格式:

 

一个正整数 N。

 

输出格式:

 

一个数,答案模 10000007 的值。

 

输入输出样例

输入样例#1: 复制
3
输出样例#1: 复制
2

说明

对于 100% 的数据,N≤10^15


Solution

数位DP

但是不是直接处理出乘积,而是枚举$i$,处理出有多少个数恰好有$i$个1.

最后直接用快速幂乘起来即可。

Code

 

#include<bits/stdc++.h>
#define LL long long
#define mod 10000007
using namespace std;

LL n;

LL mpow(LL a, LL b) {
    LL res = 1;
    for(; b; b >>= 1, a = a * a % mod)
        if(b & 1)    res = res * a % mod;
    return res;
}

LL dp[55][2][55][55];
int num[55];
LL dfs(int dep, int up, int sum, int d) {
    if(!dep && sum == d)    return dp[dep][up][sum][d] = 1;
    if(!dep)    return dp[dep][up][sum][d] = 0;
    if(~dp[dep][up][sum][d])    return dp[dep][up][sum][d];
    int tot = up ? num[dep] : 1;
    LL tmp = 0;
    for(int i = 0; i <= tot; i ++)
        tmp += dfs(dep - 1, up && i == tot, sum + i, d);
    return dp[dep][up][sum][d] = tmp;
}

LL ans[55];
LL cot(LL x) {
    int t = 0;
    memset(num, 0, sizeof(num));
    while(x) {
        num[++t] = x % 2;
        x >>= 1;
    }
    for(int i = 1; i <= 50; i ++) {
        memset(dp, -1, sizeof(dp));
        ans[i] = dfs(t, 1, 0, i);
    }
    LL res = 1;
    for(int i = 1; i <= 50; i ++)
        res = (res * mpow(i, ans[i])) % mod;
    return res;
}

int main() {
    scanf("%lld", &n);
    printf("%lld", cot(n));
    return 0;
}

 

 

 

posted @ 2018-10-23 15:10  Wans_ovo  阅读(289)  评论(0编辑  收藏  举报