[CQOI2015]选数

\[\color{red}{\text{校长者,真神人也,左马桶,右永神,会执利笔破邪炁,何人当之?}} \\ \begin{array}{|} \hline \color{pink}{\text{The principal is really a god}} \\ \color{pink}{\text{with a closestool on the left and Yongshen on the right}} \\ \color{pink}{\text{holding a sharp pen to pierce the truth}} \\ \color{pink}{\text{Who can resist him? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{green}{\text{校長は本当に神であり、左側にトイレ、右側にヨンシェンがあり}} \\ \color{green}{\text{鋭いペンを持って真実を突き刺している。誰が彼に抵抗できるだろうか? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{lightblue}{\text{Le principal est vraiment un dieu}} \\ \color{lightblue}{\text{avec des toilettes à gauche et Yongshen à droite}} \\ \color{lightblue}{\text{tenant un stylo pointu pour percer la vérité}} \\ \color{lightblue}{\text{Qui peut lui résister ? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{purple}{\text{Der Direktor ist wirklich ein Gott}} \\ \color{purple}{\text{mit einer Toilette links und Yongshen rechts}} \\ \color{purple}{\text{der einen spitzen Stift hält}} \\ \color{purple}{\text{um die Wahrheit zu durchdringen.}} \\ \color{purple}{\text{Wer kann ihm widerstehen? }} \\ \hline \end{array} \\ \begin{array}{|} \hline \color{cyan}{\text{Principalis deus est, Yongshen a dextris cum latrina}} \\ \color{cyan}{\text{acuto stylo ad perforandum veritatem: quis resistet ei? }} \\ \hline \end{array} \\ \color{red}{\text{对曰:“无人,狗欲当之,还请赐教!”}} \\ \newcommand\brak[1]{\left({#1}\right)} \newcommand\Brak[1]{\left\{{#1}\right\}} \newcommand\d[0]{\text{d}} \newcommand\string[2]{\genfrac{\{}{\}}{0pt}{}{#1}{#2}} \newcommand\down[2]{{#1}^{\underline{#2}}} \newcommand\ddiv[2]{\left\lfloor\frac{#1}{#2}\right\rfloor} \newcommand\udiv[2]{\left\lceil\frac{#1}{#2}\right\rceil} \newcommand\lcm[0]{\operatorname{lcm}} \newcommand\set[1]{\left\{{#1}\right\}} \newcommand\ceil[1]{\left\lceil{#1}\right\rceil} \newcommand\floor[1]{\left\lfloor{#1}\right\rfloor} \]

题目

传送门

正解

  注意到如果我们限制的不是 \(\gcd\) 一定是 \(k\),而是 \(k\) 的倍数,那么这个题就特别好做了,即我们可以将 \(k\) 提出来,剩下的数在 \([\udiv{L}{k},\ddiv{H}{k}]\) 之间任意选数,令 \(S=\udiv{L}{k},W=\ddiv Hk\),那么答案就是 \((W-S+1)^n\).

  这启示我们可以定义 \(f(k)\) 表示从 \([S,W]\) 中选出来的 \(n\) 个数的 \(\gcd\) 恰好为 \(d\) 的方案数,\(g(k)\) 表示从 \([S,W]\) 中选出来的 \(n\) 个数的 \(\gcd\)\(d\) 的倍数的方案数,那么存在

\[g(d)=\sum_{d\mid n}f(n)\Rightarrow f(d)=\sum_{k\mid n}g(n)\mu\brak{\frac{n}{d}} \]

  令 \(d=1\),得到

\[f(1)=\sum_{i=1}^W g(i)\mu(i)\quad\quad g(i)=\brak{\ddiv{W}{i}-\ddiv{S-1}{i}}^n \]

  后面

代码

const int maxn=2e6;
const int mod=1e9+7;

int mu[maxn+5];
int prime[maxn>>2],pcnt;
int sie[maxn+5];
inline void sieve(){
    mu[1]=1;
    rep(i,2,maxn){
        if(!sie[i])prime[++pcnt]=i,mu[i]=mod-1;
        for(int j=1;j<=pcnt && i*prime[j]<=maxn;++j){
            sie[i*prime[j]]=1;
            if(i%prime[j]==0){mu[i*prime[j]]=0;break;}
            mu[i*prime[j]]=mod-mu[i];
        }
    }
    // rep(i,1,10)printf("mu[%d] == %d\n",i,mu[i]);
    rep(i,2,maxn)mu[i]=(mu[i]+mu[i-1])%mod;
}

int n,k,A,B;

inline int qkpow(int a,int n){
    int ret=1;
    for(;n>0;n>>=1,a=1ll*a*a%mod)if(n&1)
        ret=1ll*ret*a%mod;
    return ret;
}

map<int,int>mmu;
int S(const int n){
    if(n<=maxn)return mu[n];
    if(mmu[n])return mmu[n];
    int ret=1;
    for(int l=2,r;l<=n;l=r+1){
        r=n/(n/l);
        ret=(ret+mod-1ll*(r-l+1)*S(n/l)%mod)%mod;
    }
    return mmu[n]=ret;
}

inline int S(const int l,const int r){
    int a=S(l-1),b=S(r);
    return (b+mod-a)%mod;
}

signed main(){
    sieve();
    n=readin(1),k=readin(1),A=readin(1),B=readin(1);
    A=((A-1)/k)+1,B/=k;--A;
    int ans=0;
    for(int l=1,r;l<=A;l=r+1){
        r=Min(B/(B/l),A/(A/l));
        int tmp=qkpow(B/l-A/l,n);
        ans=(ans+1ll*S(l,r)*tmp%mod)%mod;
    }
    for(int l=A+1,r;l<=B;l=r+1){
        r=B/(B/l);
        int tmp=qkpow(B/l,n);
        ans=(ans+1ll*S(l,r)*tmp%mod)%mod;
    }
    writc(ans,'\n');
    return 0;
}

  我们会发现还有一个条件我们没用上:\(H-L\le 10^5\). 这提示我们此路不通......

  于是我们更换一下做法,目的还是一样的,我们要从 \([S,W]\) 中选出 \(n\) 个互质的数,先注意到下面这个性质:

在这个区间中选取不完全相同的 \(n\) 个数,这 \(n\) 个数的 \(\gcd\) 一定小于等于他们的极差。

  证明很好证,不过要意识到却比较难。反证法即可。

  然后,设 \(dp(i)\) 表示在区间 \([S,W]\) 选择 \(n\) 个数,他们的 \(\gcd\)\(i\) 的方案数,那么就有:

\[dp(d)=\brak{\ddiv{W}{d}-\ddiv{S-1}{d}}^n-\brak{\ddiv{W}{d}-\ddiv{S-1}{d}}-\sum_{i=2}^{i\cdot d\le W} dp(i\cdot d) \]

  后面的容斥要求了我们需要从大往小进行 \(dp\),注意特殊情况是 \(S=1\) 时,所有数都可以选择 \(1\). 复杂度带上一个调和级数,最后的复杂度为 \(\mathcal O((H-L)\ln n)\).

/** @author Arextre */

#include <bits/stdc++.h>
using namespace std;

#define USING_FREAD
// #define NDEBUG
// #define NCHECK
#include <cassert>

namespace Elaina {
/** その可憐な少女は魔女であり、旅人でした。 ―― そう、私です! */

#define rep(i, l, r) for(int i = (l), i##_end_ = (r); i <= i##_end_; ++i)
#define drep(i, l, r) for(int i = (l), i##_end_ = (r); i >= i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define whole(v) ((v).begin()), ((v).end())
#define bitcnt(s) (__builtin_popcount(s))
#define rqr(x) ((x) * (x))
#define y0 FUCK_UP
#define y1 MOTHER_FUCKER
#ifdef NCHECK
# define iputs(Content) ((void)0)
# define iprintf(Content, argvs...) ((void)0)
#else
# define iputs(Content) fprintf(stderr, Content)
# define iprintf(Content, argvs...) fprintf(stderr, Content, argvs)
#endif

typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;

template<class T> inline T fab(T x) { return x < 0 ? -x : x; }
template<class T> inline void getmin(T& x, const T rhs) { x = min(x, rhs); }
template<class T> inline void getmax(T& x, const T rhs) { x = max(x, rhs); }

#ifdef USING_FREAD
inline char qkgetc() {
# define BUFFERSIZE 1 << 20
    static char BUF[BUFFERSIZE], *p1 = BUF, *p2 = BUF;
    return p1 == p2 && (p2 = (p1 = BUF) + fread(BUF, 1, BUFFERSIZE, stdin), p1 == p2) ? EOF : *p1++;
# undef BUFFERSIZE
}
# define CHARRECEI qkgetc()
#else
# define CHARRECEI getchar()
#endif

template<class T> inline T readret(T x) {
    x = 0; int f = 0; char c;
    while (!isdigit(c = CHARRECEI)) if(c == '-') f = 1;
    for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
    return f ? -x : x;
}
template<class T> inline void readin(T& x) {
    x = 0; int f = 0; char c;
    while (!isdigit(c = CHARRECEI)) if (c == '-') f = 1;
    for (x = (c ^ 48); isdigit(c = CHARRECEI); x = (x << 1) + (x << 3) + (c ^ 48));
    if (f) x = -x;
}
template<class T, class... Args> inline void readin(T& x, Args&... args) {
    readin(x), readin(args...);
}
template<class T> inline void writln(T x, char c = '\n') {
    if (x < 0) putchar('-'), x = -x;
    static int __stk[55], __bit = 0;
    do __stk[++__bit] = x % 10, x /= 10; while (x);
    while (__bit) putchar(__stk[__bit--] ^ 48);
    putchar(c);
}

} // namespace Elaina
using namespace Elaina;

const int mod = 1e9 + 7;
const int maxn = 1e5;

inline int qkpow(int a, int n) {
    int ret = 1;
    for (; n; n >>= 1, a = 1ll * a * a % mod)
        if (n & 1) ret = 1ll * ret * a % mod;
    return ret;
}
inline void chkAdd(int& x, const int& delta) { if ((x += delta) >= mod) x -= mod; }
inline void chkSub(int& x, const int& delta) { if ((x -= delta) < 0) x += mod; }
inline int plus(int x, int y) { return chkAdd(x, y), x; }
inline int subs(int x, int y) { return chkSub(x, y), x; }

int n, k, L, H, s, w;

inline void input() {
    readin(n, k, L, H);
    s = (L + k - 1) / k, w = H / k;
}

int dp[maxn + 5];
inline void solve() {
    drep (d, w - s + 1, 1) {
        dp[d] = subs(qkpow((w / d) - ((s - 1) / d), n), (w / d) - ((s - 1) / d));
        for (int j = (d << 1); j <= w - s + 1; j += d) chkSub(dp[d], dp[j]);
    }
    if (s == 1) chkAdd(dp[1], 1);
    writln(dp[1]);
}

signed main() {
    input();
    solve();
    return 0;
}

用到の一些 \(\tt trick\)

首先是使用杜教筛处理 \(\sum\mu(i)\)

其次,最重要的就是遇到 \( f(k)=\sum_{k|d}\mu({d\over k})F(d)\) 而无法继续化简时,我们可以考虑对 \(F\) 的运算动一些手脚,让 \(F'({d\over k})=F(d)\),然后再枚举 \(d\)\(k\) 的几倍,进行分块运算.

posted @ 2021-01-31 21:26  Arextre  阅读(60)  评论(0编辑  收藏  举报