bzoj3930

Description

 我们知道,从区间[L,H](L和H为整数)中选取N个整数,总共有(H-L+1)^N种方案。小z很好奇这样选出的数的最大公约数的规律,他决定对每种方案选出的N个整数都求一次最大公约数,以便进一步研究。然而他很快发现工作量太大了,于是向你寻求帮助。你的任务很简单,小z会告诉你一个整数K,你需要回答他最大公约数刚好为K的选取方案有多少个。由于方案数较大,你只需要输出其除以1000000007的余数即可。

Input

输入一行,包含4个空格分开的正整数,依次为N,K,L和H。

Output

输出一个整数,为所求方案数。

Sample Input

2 2 2 4

Sample Output

3



记录G(d)表示在L到H之间选N个数他们的gcd是d的倍数的方案,很显然答案为(H/d-(L-1)/d)^N

记录F(i)表示在L到H之间选N个数他们的gcd恰好是i的方案,那么G(d)=sigma(F(i)),其中i mod d=0且i不超过H。

通过反演可得F(i)=sigma(mu(d)*G(d/i)),其中d mod i = 0且d不超过H。


另一种是做法:

f[i]表示[L,H]之间选N个不全相同数gcd为i*k的方案数,根据第一种做法可知gcd为i*k的倍数的方案数为(H/(k*i)-(L-1)/(k*i))^N-(区间长度),那么根据容斥原理我们可以得出只要减去i*k的j倍(其中j大于1)的f[i*j]即可求出。

如果k在[l,r]中,那么是可以全部相同的。

//注:我并不太理解为什么不能把完全相同的一起求。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 100005;
const int MOD = 1000000007;
int f[MAXN], i, j, k, l, r, n;
inline int po(int x, int y)
{
	if (!y) return 1;
	int tmd = po(x, y >> 1);
	tmd = (long long)tmd * tmd % MOD;
	if (y & 1) tmd = (long long)x * tmd % MOD;
	return tmd;
}
inline void change(int &x)
{
	if (x < 0) x += MOD;
}
int main()
{
	cin >> n >> k >> l >> r;
	int N = r - l + 1;
	if (r < l) {cout << 0; return 0;};
	for(i = N; i >= 1; i --)
	{
		int ll = (l - 1) / (k * (long long)i), rr = r / (k * (long long)i);
		f[i] = po(rr - ll, n) - (rr - ll); change(f[i]);
		for(j = 2; j * i <= N; j ++)
			f[i] -= f[j * i], change(f[i]);
	}
	cout << f[1] + (l <= k && k <= r) << endl;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值