【bzoj3930】[CQOI2015]选数 莫比乌斯反演+杜教筛

题目描述

我们知道,从区间[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


题解

莫比乌斯反演+杜教筛

其中xi表示第i个数的取值。

然后这里就可以分块来求。

由于h的范围过大,所以需要使用杜教筛求mu的前缀和,详见 bzoj3944

#include <cstdio>
#include <map>
#define N 1000010
#define mod 1000000007
using namespace std;
typedef long long ll;
const int m = 1000000;
map<int , int> f;
map<int , int>::iterator it;
int mu[N] , sum[N] , prime[N] , tot;
bool np[N];
ll pow(ll x , int y)
{
	ll ans = 1;
	while(y)
	{
		if(y & 1) ans = ans * x % mod;
		x = x * x % mod , y >>= 1;
	}
	return ans;
}
int query(int n)
{
	if(n <= m) return sum[n];
	it = f.find(n);
	if(it != f.end()) return it->second;
	int i , last , ans = 1;
	for(i = 2 ; i <= n ; i = last + 1) last = n / (n / i) , ans -= (last - i + 1) * query(n / i);
	return f[n] = ans;
}
int main()
{
	int i , j , p , k , l , r , last;
	ll ans = 0;
	mu[1] = sum[1] = 1;
	for(i = 2 ; i <= m ; i ++ )
	{
		if(!np[i]) mu[i] = -1 , prime[++tot] = i;
		for(j = 1 ; j <= tot && i * prime[j] <= m ; j ++ )
		{
			np[i * prime[j]] = 1;
			if(i % prime[j] == 0)
			{
				mu[i * prime[j]] = 0;
				break;
			}
			else mu[i * prime[j]] = -mu[i];
		}
		sum[i] = sum[i - 1] + mu[i];
	}
	scanf("%d%d%d%d" , &p , &k , &l , &r) , r /= k , l = (l - 1) / k;
	for(i = 1 ; i <= r ; i = last + 1)
	{
		last = r / (r / i);
		if(l >= i) last = min(last , l / (l / i));
		ans = (ans + (query(last) - query(i - 1) + mod) % mod * pow((ll)r / i - l / i , p) % mod) % mod;
	}
	printf("%lld\n" , ans);
	return 0;
}

 

posted @ 2017-06-13 11:38  GXZlegend  阅读(445)  评论(1编辑  收藏  举报