[Luogu P4317] [BZOJ 3209] 花神的数论题

洛谷传送门
BZOJ传送门

题目背景

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

题目描述

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

输入输出格式

输入格式:

一个正整数 N N N

输出格式:

一个数,答案模 10000007 10000007 10000007 的值。

输入输出样例

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

说明

对于 100 % 100\% 100% 的数据, N ≤ 1 0 15 N≤10^{15} N1015

解题分析

数位 d p dp dp。 考虑形如 111 1 ( 2 ) 1111_{(2)} 1111(2)这样的数, 其贡献为 ( 4 ( 4 4 ) ) × ( 3 ( 4 3 ) ) × ( 2 ( 4 2 ) ) × ( 1 ( 4 1 ) ) (4^\binom{4}{4})\times (3^ \binom{4}{3})\times(2^ \binom{4}{2})\times (1^ \binom{4}{1}) (4(44))×(3(34))×(2(24))×(1(14))

于是我们枚举 1 1 1总共出现的次数, 从高往低位考虑。 例如 1101 1 ( 2 ) 11011_{(2)} 11011(2), 我们先考虑 00000 ∼ 09999 00000\sim09999 0000009999中的贡献, 然后所需的次数减 1 1 1, 考虑 10000 ∼ 10999 10000\sim10999 1000010999的贡献。 这个过程和SCOI windy数的过程差不多, 都是最后少考虑了 1 1 1的贡献, 于是先把 N + = 1 N+=1 N+=1即可。

组合数预处理的时候对 ϕ ( M O D ) \phi(MOD) ϕ(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);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值