bzoj3209 花神的數論題 數字dp

2022-02-06 04:26:36 字數 1620 閱讀 9794

題目大意:

花神的題目是這樣的 設 sum(i) 表示 i 的二進位制表示中 1 的個數。給出乙個正整數 n ,花神要問你 派(sum(i)),也就是 sum(1)—sum(n) 的乘積。

要對10000007(非質數)取模

n<=10^15

分析:o(nlogn)暴力顯然可以想出來。顯然會tle

這是從1~n乙個乙個列舉並變成二進位制算的,但是我們是否可以向普通的數字dp,一次性列舉許多個數呢?

二進位制的n,大概最多50位。例如21=10101.它顯然可以拆成二進位制下的10000+100+1

那麼,我們是否可以嘗試著去先算出來1~10..0的sum乘積?

假設這是乙個n位數,也就是有n-1個零

考慮只有乙個1的數字個數,c(n,1),即,在n個位置上,取1個位置變成1的方案數。那麼,sum[i]=1的數的貢獻就是1^c(n,1)

只有二個1,c(n-1,2),注意,是n-1位,因為除了特殊的10..000之外,其他的數都只有n-1個位置可放1。同理,sum[i]=2的數的貢獻就是2^c(n-1,2),因為本身就是連乘嘛,交換律結合律就先把不同個數的數所做的貢獻乘起來。

三個1同理。

以21=10101為例,這樣我們可以切掉n的第乙個10000以下的方案。

現在我們要處理10001~10100的方案數,我們仍然可以利用剛才處理100的思路,

只是,放乙個1在最後的三位,必然每次都要加上之前已經有過的那乙個1(10000),所以,是2^c(3,1)

2個1同理,是3^c(2,2)

這樣,就解決問題了。

**:

#includeusing

namespace

std;

typedef

long

long

ll;const

int mod=10000007

;ll ans=1

;ll sum;

ll wei;

ll c[

52][52

];ll n;

ll qm(ll x,ll y)

return

ret;

}int

main()

//直接打表就好,

//注意,組合數將作為指數部分,不能取模 c(50,25)long long也開的下,

scanf("

%lld

",&n);

wei=0;sum=0;//

sum,已經處理出來的之前的1的個數

for(wei=50;wei>=1;wei--)//

按位列舉

sum++; }}

printf(

"%lld

",ans);

return0;

}

總結:數字dp的最初思想的**,就是利用整千,整萬,整十萬的整齊特殊性質,可以利用可以想到的數學方法,對列舉進行大幅的簡化,直接減少到o(logn)

這個題其實算是數字dp的裸題,還是比較常規的。

對於其他的符合規定的第k小的數(啟示錄),是先預處理整位的情況,再按位列舉,考慮每個數能填幾,從而類似康拓展開,找到第k小的數。

或者[l,r]區間內多少個滿足限制的數,字首和思想,求l-1以內,再求r以內做差就好。

bzoj3209 花神的數論題

題意 求sum 1 sum 2 sum n 其中sum x 表示x的二進位制表達中1的數量。答案模10 7 7。n 10 15。考慮列舉sum x 的值為t,然後求有多少個不大於n的正整數的二進位制表達恰好有t個1,設這個答案為f t 這一部分可以用數字dp來實現。例如要求解1.12,可以將其分成1...

bzoj 3209 花神的數論題

設 sum i 表示 i 的二進位制表示中 1 的個數。給出乙個正整數 n 求 i 1ns um i 枚下1的個數,那麼題目就轉換成了1 n有多少個數的二進位制有x個一。這就很容易數字dp了。f i j 表示i位第一位為1,共j個一的數的數目,g i j 表示i位第一位為0,共j個一的數的數目。容易...

BZOJ3209 花神的數論題

bzoj3209 花神的數論題 背景眾所周知,花神多年來憑藉無邊的神力狂虐各大 oj oi cf tc 當然也包括 ch 啦。描述話說花神這天又來講課了。課後照例有超級難的神題啦 我等蒟蒻又遭殃了。花神的題目是這樣的 設 sum i 表示 i 的二進位制表示中 1 的個數。給出乙個正整數 n 花神要...