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

  • 相关阅读:
    spark学习进度11(RDD分区和我shuffle以及缓存)
    spark学习进度10(阶段练习)
    gradle体验笔记
    git 进阶命令
    git 基础命令
    看日记学git--笔记
    git的objects目录
    macos中gitk报错
    第5章 迪米特法则(最少知知识原则)
    操作系统概念 第9版
  • 原文地址:https://www.cnblogs.com/wangck/p/4474850.html
Copyright © 2011-2022 走看看