1. 程式人生 > >【刷題】BZOJ 3930 [CQOI2015]選數

【刷題】BZOJ 3930 [CQOI2015]選數

枚舉 () AD 方式 logs sam span mark 目的

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≤10^9,1≤L≤H≤10^9,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} \rfloor-\lfloor \frac{L-1}{n} \rfloor)^N\]


上面式子的左邊一半根據定義,右邊一半的原因如下:
\(\lfloor \frac{R}{n} \rfloor\)其實是1到R中有多少個數整除n,\(\lfloor \frac{L-1}{n} \rfloor\)類似,那麽它們相減之後,得到的就是\([L,R]\)中有多少個數可以整除n。根據題目的第一句話,我們知道選數是有序的,並且可以重復選。所以我們在得到了有多少個數整除n後,只要在裏面有序地任選N個數,方案數是\((\lfloor \frac{R}{n} \rfloor-\lfloor \frac{L-1}{n} \rfloor)^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} \rfloor-\lfloor \frac{L-1}{d} \rfloor)^N\]
改變枚舉方式
\[f(n)=\sum_{d=1}^{\lfloor \frac{R}{n} \rfloor}\mu(d)(\lfloor \frac{R}{nd} \rfloor-\lfloor \frac{L-1}{nd} \rfloor)^N\]
後面的東西整除分段加快速冪,前面的東西杜教篩
關於杜教篩,這裏只給一個式子,有興趣可以百度
\[S(n)=1-\sum_{i=2}^nS(\lfloor \frac{n}{i} \rfloor)\ \ \ \ (S(n)=\sum_{i=1}^n\mu(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='\0')
{
    if(x<0)putchar('-'),x=-x;
    if(x>9)write(x/10);
    putchar(x%10+'0');
    if(c!='\0')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),'\n');
    return 0;
}

【刷題】BZOJ 3930 [CQOI2015]選數