1. 程式人生 > >BZOJ3930 [CQOI2015]選數 【容斥】

BZOJ3930 [CQOI2015]選數 【容斥】

復雜度 pow blog 然而 兩個 小z 很快 一行 lock

題目

我們知道,從區間[L,H](L和H為整數)中選取N個整數,總共有(H-L+1)^N種方案。小z很好奇這樣選出的數的最大公約數的規律,他決定對每種方案選出的N個整數都求一次最大公約數,以便進一步研究。然而他很快發現工作量太大了,於是向你尋求幫助。你的任務很簡單,小z會告訴你一個整數K,你需要回答他最大公約數剛好為K的選取方案有多少個。由於方案數較大,你只需要輸出其除以1000000007的余數即可。

輸入格式

輸入一行,包含4個空格分開的正整數,依次為N,K,L和H。

輸出格式

輸出一個整數,為所求方案數。

輸入樣例

2 2 2 4

輸出樣例

3

提示

樣例解釋

所有可能的選擇方案:(2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4), (4, 2), (4, 3), (4, 4)

其中最大公約數等於2的只有3組:(2, 2), (2, 4), (4, 2)

對於100%的數據,1≤N,K≤10^9,1≤L≤H≤10^9,H-L≤10^5

題解

問題轉化:

從區間[L,H]中選出N個數,使得gcd為K

由於gcd為K,選出的數一定是K的倍數,令區間內所有的K的倍數除去K後,最小為L,最大為H,則問題轉化為

從區間[L,H]中選出N個數,使得gcd為1,即互質

兩個數互質的方案數除了用莫比烏斯反演之外,還有一種容斥\(O(nlogn)\)的方法
同樣可以搬到這題多個數互質上來
我們令\(f[i]\)表示gcd為i的方案數【不包括全選同一個數,這個單獨討論】
如果區間內有\(x\)

\(i\)的倍數,則粗略估計\(f[i] = x^N - x\)
我們會發現這樣算會大了,因為我們同樣包括了\(f[2*i]\)\(f[3*i]\).......
減去即可

可以證明,N以內枚舉所有數的倍數復雜度是\(O(nlogn)\)
這樣我們就可以\(O(nlogn)\)計算出\(f[1]\)
等等,還沒完,如果\(L=1\),說明全選L時gcd為1,也要考慮,此時\(ans+1\)即可

#include<cstdio>
#define LL long long int
const int P = 1000000007;
int N,K,L,H,f[100002];
int qpow(int a,int
b){ int ans = 1; for (; b; b >>= 1,a = (LL)a * a % P) if (b & 1) ans = (LL)ans * a % P; return ans % P; } int main(){ scanf("%d%d%d%d",&N,&K,&L,&H); L = L % K ? L / K + 1 : L / K; H /= K; int len = H - L + 1; for (int i = len; i; i--){ int x = H / i - (L - 1) / i; f[i] = (qpow(x,N) - x) % P; for (int j = i + i; j <= len; j += i) f[i] = (f[i] - f[j]) % P; } printf("%d\n",((f[1] + (L == 1)) % P + P) % P); return 0; }

BZOJ3930 [CQOI2015]選數 【容斥】