花神的數論題

花神的數論題

題目鏈接:luogu P4317luogu\ P4317

題目背景

衆所周知,花神多年來憑藉無邊的神力狂虐各大 OJOJOIOICFCFTCTC…… 當然也包括 CHCH 啦。

題目

話說花神這天又來講課了。課後照例有超級難的神題啦…… 我等蒟蒻又遭殃了。 花神的題目是這樣的:設 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 的值。

樣例輸入

3

樣例輸出

2

數據範圍

對於 100%100\% 的數據,N1015N≤10^{15}

思路

這道題是數位dpdp,但是我用的是組合數。

我們從大到小枚舉位數,找到是11的位數,那我們就可以讓它爲00,後面的位數就可以隨便枚舉了。接着枚舉玩這一位之後,我們只要讓它一直是11,那麼我們就可以繼續枚舉下面的,而不會重複也不會超上限來。

除了枚舉位數(設用ii枚舉),還要枚舉未確定的位數中11的個數(設用jj枚舉),那麼我們設之前以及確定來的是11的位數的數量是tt,那麼答案要乘上的值就是(t+j)Cij(t+j)^{C^j_i}(t+j)(t+j)就是整個數列中11的個數,而CijC^j_i就不用說了,組合數嘛,求出11的個數爲(t+j)(t+j)的數有多少個嘛。
(至於CijC^j_i怎麼搞,肯定就是預處理啊,因爲nn化爲二進制肯定不超過6060位,那我們只需要預處理出6060內的就可以了)

最後輸出的時候,有一點要注意,就是還要乘上計數器tt。因爲按上面那樣的話是不會算到nn本身的情況的,所以我們輸出的時候還要再乘上tt纔可以。

還有一個很重要的東西,因爲1000000710000007它不是素數,所以我們預處理組合數的時候就不可以用1000000710000007,而是要用它的歐拉函數值。
φ(10000007)=9988440\varphi(10000007)=9988440

代碼

#include<cstdio>
#define mo 10000007
#define ll long long

using namespace std;

ll n, C[61][61], t, ans = 1;

ll qsm(ll a, ll b) {//快速冪
	ll anan = 1;
	while (b) {
		if (b & 1) anan = (anan * a) % mo;
		a = (a * a) % mo;
		b >>= 1;
	}
	return anan;
}

int main() {
	for (int i = 0; i <= 60; i++)//預處理組合數
		C[i][0] = 1;
	for (int i = 1; i <= 60; i++)
		for (int j = 1; j <= i; j++)
			C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % 9988440;//因爲10000007不是素數,所以要用它的歐拉函數值phi(10000007)=9988440
	
	scanf("%lld", &n);//讀入
	
	for (int i = 60; i >= 0; i--) {//枚舉沒確定位數
		if (!((n >> i) & 1)) continue;//當前位是0
		for (int j = 0; j <= i; j++)//枚舉沒確定的1的個數
			if (t + j)//的出來的至少有一個1
				ans = (ans * qsm(t + j, C[i][j])) % mo;//乘起來
		t++;//加計數器
	}
	
	printf("%lld", ans * t % mo);//輸出(要記得乘上計數器)
	
	return 0;
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章