luogu_4317: 花神的数论题
Posted zxytxdy
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了luogu_4317: 花神的数论题相关的知识,希望对你有一定的参考价值。
花神的数论题
题意描述:
- 设(sum(i))表示(i)的二进制数中(1)的个数。
- 给定一个整数(N),求(prod_{i=1}^Nsum(i))。
输入描述:
- 输入包含一个正整数(N(Nleq10^{15}))。
输出描述:
- 一个数,答案模(10000007)的值。
解题思路:
- 数位(dp)+快速幂。
- 令(f(i,j,k))表示以(k)开头的(i)位数中(1)的个数为(j)的数量。有转移方程
- (f(i,j,0)=f(i-1,j,0)+f(i-1,j,1))
- (f(i,j,1)=f(i-1,j-1,0)+f(i-1,j-1,1))
- 这个很好理解,就是往最高位填(0/1)。
- 设(sum(i)==x)的(i)有(tot)个,那么他对答案的贡献显然就是(x^{tot})。
- 所以这时候枚举(x)就行,显然对于(N(Nleq 10^{15}))而言,不会枚举超过(60)次。
- 详见代码:
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 70, mod = 10000007;
ll f[maxn][maxn][2];
ll n, ans;
inline ll qmi(ll a, ll b)
{
ll res = 1;
while(b)
{
if(b & 1) res = (res * a) % mod;
a = (a * a) % mod;
b >>= 1ll;
} return res % mod;
}
//x对应的二进制中有多少个1
inline ll len(ll x)
{
ll res = 0;
while(x)
{
if(x & 1ll) res++;
x >>= 1ll;
}
return res;
}
int num[maxn], cnt;
inline void work(ll x)
{
cnt = 0; ans = 1;
while(x) //分解数位
{
num[++cnt] = x&1;
x >>= 1;
}
//枚举m, 求出有多少i,有sum(i)=m
for(int m = 1; m <= cnt; m++)
{
for(int i = 1; i < cnt; i++)
ans = (ans * qmi(m, f[i][m][1]) % mod) % mod;
//先枚举位数比n要小的数
//开始处理位数和n相同的数字
int k = m;
//如果n的最高位是1的话
//其实相当于把最高位固定了
if(num[cnt]) k--;
//从高位往低位枚举
//之后我们让第i位小于n的第i位
//这样可以让第i位后面的数字随便填写
for(int i = cnt - 1; i >= 1; i--)
{
for(int j = 0; j < num[i]; j++)
ans = (ans * qmi(m, f[i][k][j]) % mod) % mod;
if(num[i]) k--; //相当于把第i位固定 因为i有1 所以k--
if(k < 0) break;
}
}
//由于一直卡上界,所以其实一直没有遇到等于n的情况
//所以最后对n暴力分解数位处理一下
ans = ((ans % mod) * (len(n) % mod)) % mod;
printf("%lld
", ans);
}
inline void init()
{
scanf("%lld", &n);
//dp预处理
f[1][0][0] = f[1][1][1] = 1;
for(int i = 1; i <= 60; i++)
{
for(int j = 0; j <= i; j++)
{
f[i+1][j+1][1] += f[i][j][0] + f[i][j][1];
f[i+1][j][0] += f[i][j][0] + f[i][j][1];
}
} work(n);
}
int main()
{
init();
return 0;
}
以上是关于luogu_4317: 花神的数论题的主要内容,如果未能解决你的问题,请参考以下文章