1. 程式人生 > >Luogu P3172 [CQOI2015]選數

Luogu P3172 [CQOI2015]選數

[1] 采集 noi www. register 公約數 ++i 根據 noi2010

這題的反演做法好像很不可食用啊還得套一個杜教篩

我們註意到題目一個重要的性質:\(H-L\le10^5\),看起來可以好好利用一下。

我們首先轉化問題,類似於許多和\(\gcd\)有關的問題,我們將原來的最大公約數\(K\)想辦法變成\(1\)

這個怎麽處理呢,其實很簡單,將\(L\)變為\(\lceil \frac{L}{K}\rceil\),將\(R\)變為\(\lfloor\frac{H}{K}\rfloor\)

顯然這樣我們把問題轉化為:在\([L,H]\)種取\(N\)次數使它們的\(\gcd\)\(1\)

然後我們考慮計算一個\(f_i\),表示選出的數的\(\gcd\)\(i\)

,且選出的所有數不全相同的方案數。

那麽我們借Luogu P1447 [NOI2010]能量采集的思路,考慮容斥計算\(f_i\)

我們首先求出\([L,H]\)之間\(i\)的倍數\(x\),那麽先令\(f_i=x^N-x\)

但是發現這樣的方案只是含有公約數,因此我們還要減去\(f_{ki}(k\in N^+,k>1)\)的答案

然後做法很明顯了,倒序枚舉並計算即可,復雜度根據調和級數公式為\(O(n(\ln(n)+H_n))\)

還有註意一下\(L=1\)時所有的數都可以選\(1\),因此要特判。

CODE

#include<cstdio>
#define RI register int
using namespace std;
const int N=100005,mod=1000000007;
int n,k,L,R,l,r,f[N];
inline void dec(int &x,int y)
{
    if ((x-=y)<0) x+=mod;
}
inline int quick_pow(int x,int p,int mul=1)
{
    for (;p;p>>=1,x=1LL*x*x%mod) if (p&1) mul=1LL*mul*x%mod; return mul;
}
int main()
{
    RI i,j; scanf("%d%d%d%d",&n,&k,&L,&R);
    L=L%k?L/k+1:L/k; R=R/k; for (i=1;i<=R-L;++i)
    {
        int l=L%i?L/i+1:L/i,r=R/i;
        f[i]=quick_pow(r-l+1,n); dec(f[i],r-l+1);
    }
    for (i=R-L;i;--i) for (j=i<<1;j<=R-L;j+=i) dec(f[i],f[j]);
    return printf("%d",L^1?f[1]:(f[1]+1)%mod),0;
}

Luogu P3172 [CQOI2015]選數