zoukankan      html  css  js  c++  java
  • CQOI2015 选数

    题目

    ([L, H])(H-Lleq 10^5))选出(n)个整数,使得这些数的最大公约数为(k)的方案数。

    算法

    首先有一个很简单的转化,原问题可以简化为:

    ([lceil {frac L k} ceil, lfloor {frac H k} floor])中选出(n)个整数,使得这些数的最大公约数为(1)的方案数。

    下面,(L)的意义不再是原题的意义了,而是(lceil {frac L k} ceil)(H)同理。

    算法1

    (dp(i))为从选出的这些数最大公约数的为(i)的方案数。那么我们可以得到:

    [dp(i)=(lfloor {frac H i} floor - lfloor {frac {L - 1} i} floor)^n-sum_{i|j,i < j}dp(j) ]

    然后我们就这样DP就有(80)分了,时间复杂度(O(H))这里的(H)不是指的是题目中的(H),而是重新定义的(H))。

    算法2

    对上面的DP进行莫比乌斯反演。

    (F(i))为选出的数的最大公约数能够被(i)整除的方案数,那么:

    [F(i)=sum_{i|d}dp(d) ]

    反演得:

    [dp(d)=sum_{d|i}mu(frac i d) F(i) ]

    我们求的是(dp(1)),所以(d=1)

    [dp(1)=sum_{i=1}^Hmu(i) F(i)=sum_{i=1}^Hmu(i)(lfloor {frac H i} floor - lfloor {frac {L - 1} i} floor)^n ]

    这个算起来也是(O(H))的,但是我们还可以继续化简下去。注意到题目中的条件(H-Lleq 10^5)

    (i geq H-(L-1)),即(i > H - L)的时候,我们可以发现一个很神奇的东西,那就是(lfloor {frac H i} floor - lfloor {frac {L - 1} i} floor)要么等于(0),要么等于(1),所以我们可以把指数去掉!

    [dp(1)=sum_{i=1}^{H-L}mu(i)(lfloor {frac H i} floor - lfloor {frac {L - 1} i} floor)^n+sum_{i=H-L+1}^Hmu(i)(lfloor {frac H i} floor - lfloor {frac {L - 1} i} floor) ]

    加号左边的式子我们可以暴力算出,现在问题是右边那个怎么算。我们可以计算它的补集:

    [sum_{i=H-L+1}^Hmu(i)(lfloor {frac H i} floor - lfloor {frac {L - 1} i} floor)=sum_{i=1}^Hmu(i)(lfloor {frac H i} floor-lfloor {frac {L - 1} i} floor)-sum_{i=1}^{H-L}mu(i)(lfloor {frac H i} floor - lfloor {frac {L - 1} i} floor) ]

    减号右边的式子我们又可以暴力算出,而左边的,注意到它就是原问题,不过此时(n=1)

    原问题:(sum_{i=1}^Hmu(i)(lfloor {frac H i} floor - lfloor {frac {L - 1} i} floor)^n)
    现在我们求的:(sum_{i=1}^Hmu(i)(lfloor {frac H i} floor-lfloor {frac {L - 1} i} floor))

    这样,我们的问题是:从([L, H])中选出(1)个整数,使得这些数的最大公约数为(1)的方案数。

    这个问题的答案就是(sum_{i=1}^Hmu(i)(lfloor {frac H i} floor-lfloor {frac {L - 1} i} floor)),若(L=1),那么式子的值就是(1),否则就是(0)

    至此,我们就巧妙地解决这道题,时间复杂度(O(H-L))

    代码

    #include <cstdio>
    #include <iostream>
    using namespace std;
    
    typedef long long i64;
    
    const int MAXN = (int) 1e5 + 3;
    const int MOD = (int) 1e9 + 7;
    
    int H, L, n;
    
    int myPower(int a, int b) {
    	int ans = 1;
    	while (b) {
    		if (b & 1) 
    			ans = (i64) ans * a % MOD;
    		a = (i64) a * a % MOD;
    		b >>= 1;
    	}
    	return ans;
    }
    
    int main() {
    	freopen("number.in", "r", stdin);
    	freopen("number.out", "w", stdout);
    
    	int k;
    	scanf("%d%d%d%d", &n, &k, &L, &H);
    	L = (L + k - 1) / k;
    	H = H / k;
    	int HL = H - L;
    
    	static bool notPrime[MAXN];
    	static int prime[MAXN], cntPrime;
    	static int mu[MAXN];
    	static int fac[MAXN];
       
    	mu[1] = 1;
    	for (int i = 2; i <= HL; i ++) {
    		if (! notPrime[i]) {
    			prime[cntPrime ++] = i;
    			mu[i] = -1;
    			fac[i] = i;
    		}
    		for (int k = 0; k < cntPrime; k ++) {
    			int j = prime[k];
    			if (j > fac[i]) break;
    			if ((i64) j * i > HL) break;
    			notPrime[j * i] = true;
    			mu[j * i] = fac[i] == j ? 0 : mu[i] * -1;
    			fac[j * i] = j;
    		}
    	}
    
    	i64 ans = 0;
    	for (int i = 1; i <= HL; i ++) {
    		ans += (i64) mu[i] * myPower(H / i - (L - 1) / i, n);
    		ans %= MOD;
    	}
    	if (L == 1) ans ++;
    	for (int i = 1; i <= HL; i ++) {
    		ans -= mu[i] * (H / i - (L - 1) / i);
    		ans %= MOD;
    	}
    	cout << (ans + MOD) % MOD << endl;
    
    	return 0;
    }
    
    

    算法3

    Orz

    (f(i))为选出的数的最大公约数为(i)且选出的这些数不能全部是同一个数的方案数。

    然后我们又可以得到:$$f(i)=(lfloor {frac H i} floor - lfloor {frac {L - 1} i} floor)^n-(lfloor {frac H i} floor - lfloor {frac {L - 1} i} floor)-sum_{i|j,i < j}f(j)$$
    可以发现,这里的(i)最大只有(H-L),因为对于任意正整数(x,y(x eq y)),都有(gcd(x, y)leq |x-y|)

    然后我们要加上允许全部数选同一个数的方案。

    算法4

    Orz

  • 相关阅读:
    LeetCode(287)Find the Duplicate Number
    LeetCode(290) Word Pattern
    LeetCode(205)Isomorphic Strings
    LeetCode(201) Bitwise AND of Numbers Range
    LeetCode(200) Number of Islands
    LeetCode(220) Contains Duplicate III
    LeetCode(219) Contains Duplicate II
    命令行执行Qt程序
    LeetCode(228) Summary Ranges
    redis 的安装和使用记录
  • 原文地址:https://www.cnblogs.com/wangck/p/4474850.html
Copyright © 2011-2022 走看看