bzoj 3930: [CQOI2015]选数

Description

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

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

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

解题报告:老套路设\(f_i\)表示gcd为i的方案数,转化为求[L/K,R/K]中互质的数对个数,答案即为\(f_1\),考虑这一题数据范围较大,且\(H-L<=10^5\),那么就需要用到结论:假设N个数不全相同,那么他们的最大公约数小于最大和最小的两个数之差,正确性显然,所以我们把状态改为:\(f_i\)表示选n个互不相同的数,gcd为i的方案数
令L=L/K,R=R/K
那么[L,R]间含i这个因子的数有(R/i)-((L-1)/i)种,所以选n个的方案数一共有\((R/i-(L-1)/i)^n\),再减去所有数都重复的\((R/i-(L-1)/i)\)种,减完之后的值设为\(tot\),然后就是推\(fi\),因为现在的方案只是包含i这个因子,而不是gcd=i的方案,所以还要减去gcd不为i的方案
综上:\(f_i=tot-\sum_{j=i*2}^{j=R-L}f_j\)

#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cmath>
#define RG register
#define il inline
#define iter iterator
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const int N=1e5+5,mod=1000000007;
ll qm(ll x,ll k){
	ll sum=1;
	while(k){
		if(k&1)sum*=x,sum%=mod;
		x*=x;x%=mod;k>>=1;
	}
	return sum;
}
ll f[N];
void work()
{
	int n,L,R,K;
	cin>>n>>K>>L>>R;
	bool flg=(L<=K && K<=R);
	L--;L/=K;R/=K;int c=R-L;
	ll l,r;
	for(int i=c;i>=1;i--){
		l=L/i;r=R/i;
		f[i]=((qm(r-l,n)-(r-l)%mod)+mod)%mod;
		for(int j=2;j*i<=c;j++)
			f[i]-=f[j*i],f[i]=(f[i]%mod+mod)%mod;
	}
	f[1]+=flg;f[1]%=mod;
	printf("%lld\n",f[1]);
}

int main()
{
	work();
	return 0;
}

posted @ 2017-09-02 14:48  PIPIBoss  阅读(92)  评论(0编辑  收藏  举报