luogu P3172 [CQOI2015]选数 |容斥原理

题目描述

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

由于方案数较大,你只需要输出其除以 \(10^9+7\) 的余数即可。

输入格式

输入一行,包含四个空格分开的正整数,依次为 \(N,K,L,H\)

输出格式

输出一个整数,为所求方案数除以 \(10^9 + 7\) 的余数。


\(f[i]\)表示选出的数的最大公约数\(i\)且选出的数不全相同的方案数


#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define int long long
const int N=1e5+5,mod=1e9+7;
inline int read(){
    int x=0,f=1; char ch=getchar();
    while(ch>'9'||ch<'0'){ if(ch=='-')f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }
    return x*f;
}
int f[N];
inline int ksm(int a, int b) {
    int res=1;
    while(b){
        if(b&1)res=res*a%mod;
        a=a*a%mod; b>>=1;
    }
    return res;
}
signed main(){
	int n=read(),k=read(),l=read(),h=read();
	if(l%k)l=l/k+1; else l/=k;	h/=k;
	if(l>h){ puts("0"); return 0; }
	for(int i=1;i<=h-l;i++){
		int L=l,R=h;
		if(L%i)L=L/i+1; else L/=i; R/=i;
		if(L>R)continue;
		f[i]=(ksm(R-L+1,n)-(R-L+1)+mod)%mod;
	}
	for(int i=h-l;i;i--) for(int j=(i<<1);j<=h-l;j+=i)f[i]=(f[i]-f[j]+mod)%mod;
	if(l==1)f[1]=(f[1]+1)%mod;
	cout<<f[1]<<endl;
	return 0;
}
posted @ 2020-06-11 19:35  白木偶君  阅读(206)  评论(2编辑  收藏  举报