zoukankan      html  css  js  c++  java
  • 【刷题】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

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

    Sample Input

    2 2 2 4

    Sample Output

    3

    HINT

    样例解释
    所有可能的选择方案:(2, 2), (2, 3), (2, 4), (3, 2), (3, 3), (3, 4), (4, 2), (4, 3), (4, 4)
    其中最大公约数等于2的只有3组:(2, 2), (2, 4), (4, 2)
    对于100%的数据,1≤N,K≤109,1≤L≤H≤109,H-L≤10^5

    Solution

    我用的是莫比乌斯反演加杜教筛
    这题本来不需要莫反的,但最近都在练习莫反,那就用莫反做了
    (F(d))代表在([L,R])中选N个数,它们的gcd为d及其倍数的方案数
    (f(d))代表在([L,R])中选N个数,它们的gcd为d的方案数

    [F(n)=sum_{n|d}f(d)=(lfloor frac{R}{n} floor-lfloor frac{L-1}{n} floor)^N ]

    上面式子的左边一半根据定义,右边一半的原因如下:
    (lfloor frac{R}{n} floor)其实是1到R中有多少个数整除n,(lfloor frac{L-1}{n} floor)类似,那么它们相减之后,得到的就是([L,R])中有多少个数可以整除n。根据题目的第一句话,我们知道选数是有序的,并且可以重复选。所以我们在得到了有多少个数整除n后,只要在里面有序地任选N个数,方案数是((lfloor frac{R}{n} floor-lfloor frac{L-1}{n} floor)^N),它们可以保证它们的gcd一定为n或n的倍数
    接下来继续推

    [f(n)=sum_{n|d}mu(frac{d}{n})F(d)=sum_{n|d}mu(frac{d}{n})(lfloor frac{R}{d} floor-lfloor frac{L-1}{d} floor)^N ]

    改变枚举方式

    [f(n)=sum_{d=1}^{lfloor frac{R}{n} floor}mu(d)(lfloor frac{R}{nd} floor-lfloor frac{L-1}{nd} floor)^N ]

    后面的东西整除分段加快速幂,前面的东西杜教筛
    关于杜教筛,这里只给一个式子,有兴趣可以百度

    [S(n)=1-sum_{i=2}^nS(lfloor frac{n}{i} floor) (S(n)=sum_{i=1}^nmu(i)) ]

    #include<bits/stdc++.h>
    #define ll long long
    const int Mod=1e9+7,MAXN=1e6+10,inf=0x3f3f3f3f;
    int prime[MAXN],cnt,vis[MAXN],s[MAXN],mu[MAXN];
    std::map<ll,ll> M;
    template<typename T> inline void read(T &x)
    {
    	T data=0,w=1;
    	char ch=0;
    	while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
    	if(ch=='-')w=-1,ch=getchar();
    	while(ch>='0'&&ch<='9')data=((T)data<<3)+((T)data<<1)+(ch^'0'),ch=getchar();
    	x=data*w;
    }
    template<typename T> inline void write(T x,char c='')
    {
    	if(x<0)putchar('-'),x=-x;
    	if(x>9)write(x/10);
    	putchar(x%10+'0');
    	if(c!='')putchar(c);
    }
    template<typename T> inline void chkmin(T &x,T y){x=(y<x?y:x);}
    template<typename T> inline void chkmax(T &x,T y){x=(y>x?y:x);}
    template<typename T> inline T min(T x,T y){return x<y?x:y;}
    template<typename T> inline T max(T x,T y){return x>y?x:y;}
    inline void init()
    {
    	memset(vis,1,sizeof(vis));
    	vis[0]=vis[1]=0;
    	mu[1]=1;
    	for(register int i=2;i<MAXN;++i)
    	{
    		if(vis[i])
    		{
    			prime[++cnt]=i;
    			mu[i]=-1;
    		}
    		for(register int j=1;j<=cnt&&i*prime[j]<MAXN;++j)
    		{
    			vis[i*prime[j]]=0;
    			if(i%prime[j])mu[i*prime[j]]=-mu[i];
    			else break;
    		}
    	}
    	for(register int i=1;i<MAXN;++i)s[i]=s[i-1]+mu[i];
    }
    inline ll qexp(ll a,ll b)
    {
    	ll res=1;
    	while(b)
    	{
    		if(b&1)res=res*a%Mod;
    		a=a*a%Mod;
    		b>>=1;
    	}
    	return res;
    }
    inline ll MuSum(int x)
    {
    	if(x<MAXN)return s[x];
    	if(M[x])return M[x];
    	ll res=1;
    	for(register int i=2;;)
    	{
    		if(i>x)break;
    		int j=x/(x/i);
    		res-=(ll)(j-i+1)*(ll)MuSum(x/i);
    		i=j+1;
    	}
    	return M[x]=res;
    }
    inline ll solve(int N,int L,int H)
    {
    	ll res=0;
    	for(register int i=1;;)
    	{
    		if(i>H)break;
    		int j=min(H/(H/i),L/i?L/(L/i):inf);
    		(res+=qexp((ll)(H/i-L/i),N)*(ll)(MuSum(j)-MuSum(i-1))%Mod)%=Mod;
    		i=j+1;
    	}
    	return (res+Mod)%Mod;
    }
    int main()
    {
    	init();
    	int N,K,L,H;
    	read(N);read(K);read(L);read(H);
    	write(solve(N,(L-1)/K,H/K),'
    ');
    	return 0;
    }
    
  • 相关阅读:
    2011年03月28日
    如何响应UIScrollView的touchesBegan和touchesEnd消息
    使用Git在Mac和Windows系统之间进行同步数据
    ActionScript 3.0 编程 中文版PDF下载地址
    WIN7 英文 语言包(KB972813)/多国语言包下载(转)
    如何根据内容和字体调整UILabel的大小
    xcode 快捷键(转)
    VMware, Win7, Mac系统之间使用Git版本控制器的解决方案
    iOS 开发教程资源列表(转载)
    取消UITableViewCell高亮颜色
  • 原文地址:https://www.cnblogs.com/hongyj/p/8540447.html
Copyright © 2011-2022 走看看