首页 > 其他分享 >数学——数论/杂项

数学——数论/杂项

时间:2024-04-13 23:44:44浏览次数:25  
标签:lfloor frac limits 数论 sum rfloor int 数学 杂项

狄利克雷卷积

前置

下取整的性质

$\left\lfloor\frac {\lfloor\frac{a}{b}\rfloor}{c}\right\rfloor=\lfloor \frac{a}{bc} \rfloor$。 证明略。

上取整的性质

$\left\lceil\frac {\lceil\frac{a}{b}\rceil}{c}\right\rceil= \lceil \frac{a}{bc} \rceil$

证明:

$\left\lceil\frac {\lceil\frac{a}{b}\rceil}{c}\right\rceil=\left\lfloor\frac {\lfloor\frac{a+b-1}{b}\rfloor+c-1}{c}\right\rfloor=\left\lfloor\frac {\lfloor\frac{a+b-1+bc-b}{b}\rfloor}{c}\right\rfloor=\left\lfloor\frac {a+bc-1}{bc}\right\rfloor= \lceil \frac{a}{bc} \rceil$

$g=\mu*f$,已知$f$求出$g$ 

这里有三种方法

方法1
 for (int i = 1; i <= n; i++) g[i] = 0;
for (int i = 1; i <= n; i++) {
    for (int j = 1; i * j <= n; j++) {
        g[i * j] = (g[i * j] + mu[i] * f[j]) % mod;
    }
}
// 依照定义,O(nlogn)
方法2
 for (int i = 1; i <= N; i++) g[i] = f[i];
for (int i = 1; i <= N; i++) {
    for (int j = 2; i * j <= N; j++) {
        g[i * j] = (g[i * j] - g[i]) % MOD;
    }
}
// 类似求狄利克雷卷积逆的方式,不需要线性筛 mu ,O(nlogn)
方法3
 for (int i = 1; i <= n; i++) g[i] = f[i];
for (auto i : p) {
    for (int j = n / i; j >= 1; j--) {
        g[i * j] = (g[i * j] - g[j]) % MOD;
    }
}//O(nloglogn)

第三种可以理解为$dp$,设$g(i,n)=\sum\limits_{d|n,d只含前i种质因子}f(d)\mu(\frac{n}{d})$

那么$g(i,n)=\begin{cases} g(i-1,n), \;\;\;p_i\nmid n \\g(i-1,n)-g(i-1,\frac{n}{p_i}),\;\;\;p_i|n \end{cases}$

 

筛法

埃氏筛

枚举每个数,筛去它们的整数倍。

复杂度$O(nloglogn)$ ,证明略。

 

线性筛(欧拉筛)

枚举每个数$x$,筛去它们的$p_i$倍($p_i$为质数)。当$x\%p_i=0$时,不需要再往下筛直接$break$,因为$p_i$是递增的,所以$x$乘上其他的质数的结果一定会被$p_i$的倍数筛掉。显然这样每个数只会被筛一次,故复杂度为$O(n)$。

任何积性函数都可以进行线性筛,所有线性筛积性函数都是基于线性筛质数的。

当然完全积性函数也可以线性筛,并且更加容易。不管$i$与$p$是否互质,都有$f(i\cdot p)=f(i) \cdot f(p)$,直接筛就行了。

若想线性筛出积性函数$f(x)$,就必须快速计算出以下函数值,$f(1),f(p),f(p^k)$,其中$p$为质数。

考虑筛的过程中,$i\cdot p$会被$i$乘上$p$给筛掉,将$i$唯一分解得到$p_1^{c_1}p_2^{c_2}\cdots p_k^{c_k}$,其中$p_1\le p_2\le \cdots\le p_k$

则一定有$p\leq p_1$,这是显然的,因为一旦第一次遇到$i\%p=0$也就是最小质因子时,直接$break$了。

如果$p< p_1$,那么$p$与$i$互质,可以直接得到$f(i\cdot p)=f(i)f(p)$

如果$p=p1$,这是需要对$i$记录一个$low_i$,表示$i$中最小质因子的指数次幂,即$low_i=p_1^{c_1}$

如果$i$除掉$low_i$,那么结果中最小质因子一定大于$p_1$,从而得到$gcd(\frac{i}{low_i},low_i\cdot p)=1$,那么$f(i\cdot p)=f(\frac{i}{low_i})f(low_i\cdot p)$

 

举例

欧拉函数$\varphi$,$\varphi(1)=1$,$\varphi(p)=p-1$。当$i\%p\neq0$时,$i$和$p$互质,$\varphi(i\cdot p)=\varphi(i)\cdot \varphi(p)=\varphi(i)\cdot(p-1)$;当$i\%p=0$时,$\varphi(i\cdot p)=\varphi(\frac{i}{p^c})\varphi(p^{c+1})=\frac{\varphi(i)}{p^c(1-\frac1{p})}p^{c+1}(1-\frac{1}{p})=p\cdot \varphi(i)$

查看代码
 void init(int mod = MOD) {

    np[0] = np[1] = 1;phi[1] = 1;
    for (int i = 2;i < N;i++) {
        if (!np[i]) {
            p.push_back(i);
            phi[i] = i - 1;
        }
        for (auto j : p) {
            if (i * j < N) {
                np[i * j] = 1;
                if (i % j == 0) {
                    phi[i * j] = phi[i] * j;
                    break;
                }
                else {
                    phi[i * j] = phi[i] * (j - 1);
                }
            }
            else {
                break;
            }
        }
    }
}

莫比乌斯函数$\mu$,$\mu(1)=1$,$\mu(p)=-1$。当$i\%p\neq0$时,$i$和$p$互质,$\mu(i\cdot p)=\mu(i)\cdot \mu(p)=-\mu(i)$;当$i\%p=0$时,$\mu(i\cdot p)=\mu(\frac{i}{p^c})\mu(p^{c+1})=\mu(\frac{i}{p^c})\cdot 0=0$

查看代码
 void init(int mod = MOD) {

    np[0] = np[1] = 1;mu[1] = 1;
    for (int i = 2;i < N;i++) {
        if (!np[i]) {
            p.push_back(i);
            mu[i] = -1;
        }
        for (auto j : p) {
            if (i * j < N) {
                np[i * j] = 1;
                if (i % j == 0) {
                    mu[i] = 0;
                    break;
                }
                else {
                    mu[i * j] = -mu[i];
                }
            }
            else {
                break;
            }
        }
    }
}

约数个数函数$d$,$d(1)=1$,$d(p)=2$ 。当$i\%p\neq0$时,$i$和$p$互质,$d(i\cdot p)=d(i)\cdot d(p)=2d(i)$;当$i\%p=0$时,$d(i\cdot p)=d(\frac{i}{p^c})d(p^{c+1})=\frac{d(i)}{c+1}(c+2)$

约数和函数$\sigma$,$\sigma(1)=1$,$\sigma(p)=p+1$,当$i\%p\neq0$时,$i$和$p$互质,$\sigma(i\cdot p)=\sigma(i)\cdot \sigma(p)=(p+1)\sigma(i)$;当$i\%p=0$时,$\sigma(i\cdot p)=\sigma(\frac{i}{p^c})\sigma(p^{c+1})=\frac{\sigma(i)}{\sum\limits_{i=0}^{c}p^i}\sum\limits_{i=0}^{c+1}p^i$

查看代码
 void init(int mod = MOD) {

    np[0] = np[1] = 1;
    sigma[1] = 1;low[1] = 1;
    for (int i = 2;i < N;i++) {
        if (!np[i]) {
            p.push_back(i);
            sigma[i] = i + 1;low[i] = i;
        }
        for (auto j : p) {
            if (i * j < N) {
                np[i * j] = 1;
                if (i % j == 0) {
                    low[i * j] = low[i] * j;
                    if (i == low[i]) sigma[i * j] = sigma[i] * j + 1;
                    else sigma[i * j] = sigma[i / low[i]] * sigma[low[i] * j];
                    break;
                }
                else {
                    low[i * j] = j;
                    sigma[i * j] = sigma[i] * (j + 1);
                }
            }
            else {
                break;
            }
        }
    }
}

下面这个不是积性函数,但是可以用来求解上面的东西,并且也可以线性筛。

最小次幂函数$low$,即$n=p_1^{c_1}p_2^{c_2}...p_k^{c_k}$,$p_1<p_2<...<p_k$,那么$low(n)=p_1^{c_1}$。

当$i\%p\neq0$时,$i$中最小质因子大于$p$,$low(i\cdot p)=p$;当$i\%p=0$时,$i$中最小质因子为$p$,$low(i\cdot p)=p\cdot low(i)$

杜教筛

模板题:P4213 - 杜教筛

在亚线性时间内求出某些特征积性函数的前缀和

所谓某些特征积性函数,指的是存在一个对应的积性函数$g$,使得$f*g=h$,且$g$,$h$的前缀和可以$O(1)$得知的函数$f$ 。

记$F,G,H$分别为$f,g,h$的前缀和,那么$H(n)=\sum\limits_{i=1}^{n}\sum\limits_{d|n}f(\frac{i}{d})g(d)=\sum\limits_{d=1}^{n}g(d)\sum\limits_{j=1}^{\lfloor \frac{n}{d} \rfloor}f(j)$

$=\sum\limits_{d=1}^{n}g(d)F(\lfloor \frac{n}{d} \rfloor)=g(1)F(n)+\sum\limits_{d=2}^{n}g(d)F(\lfloor \frac{n}{d} \rfloor)$

由于$g$为积性函数,那么$g(1)=1$,于是就有$F(n)=H(n)-\sum\limits_{d=2}^{n}g(d)F(\lfloor \frac{n}{d} \rfloor)$

注意到,如果从小到大筛的话,右边的$F$是已知的,而$G,H$都可以$O(1)$求得,则对$\lfloor\frac{n}{d} \rfloor$整除分块,单论复杂度$O(\sqrt{n})$

事实上,我们一般只关心一个点$n$的$F(n)$,故该式子一般递归求解。预处理$n^{\frac{2}{3}}$内的$f$值时,可以取得最优复杂度。

 

举例

求$\mu$的前缀和,注意到$\mu*1=\varepsilon$,于是有$\sum\limits_{i=1}^{n}\mu(i)=\sum\limits_{i=1}^{n}\varepsilon(i)-\sum\limits_{d=2}^{n}\sum\limits_{i=1}^{\lfloor \frac{n}{d} \rfloor}\mu(i)=1-\sum\limits_{d=2}^{n}\sum\limits_{i=1}^{\lfloor \frac{n}{d} \rfloor}\mu(i)$

求$\varphi$的前缀和,注意到$\varphi*1=id$,$\sum\limits_{i=1}^{n}\varphi(i)=\sum\limits_{i=1}^{n}id(i)-\sum\limits_{d=2}^{n}\sum\limits_{i=1}^{\lfloor \frac{n}{d} \rfloor}\varphi(i)=\frac{n(n+1)}{2}-\sum\limits_{d=2}^{n}\sum\limits_{i=1}^{\lfloor \frac{n}{d} \rfloor}\varphi(i)$

求$id\cdot\mu$的前缀和,我们需要找到合适的函数$g$,注意到$id*id=\sum\limits_{d|n}d\frac{n}{d}=\sum\limits_{d|n}n=nd(n)$

不妨令$g=id$尝试一下。于是有$(id\cdot \mu)*id=\sum\limits_{d|n}d\mu(d)\frac{n}{d}=n\sum\limits_{d|n}\mu(d)=n(\mu*1)=n\cdot \varepsilon(n)=\varepsilon(n)$

所以$\sum\limits_{i=1}^{n}id\cdot\mu(i)=\sum\limits_{i=1}^{n}\varepsilon(i)-\sum\limits_{d=2}^{n}\sum\limits_{i=1}^{\lfloor \frac{n}{d} \rfloor}id\cdot\mu(i)=1-\sum\limits_{d=2}^{n}\sum\limits_{i=1}^{\lfloor \frac{n}{d} \rfloor}id\cdot\mu(i)$

求$id\cdot\varphi$的前缀和,我们需要找到合适的函数$g$,与上面等同,$g=id$,于是有$(id\cdot \varphi)*id=\sum\limits_{d|n}d\varphi(d)\frac{n}{d}=n\sum\limits_{d|n}\varphi(d)=n(\varphi*1)=n\cdot id(n)=n^2=id_2(n)$

所以$\sum\limits_{i=1}^{n}id\cdot\varphi(i)=\sum\limits_{i=1}^{n}id_2(i)-\sum\limits_{d=2}^{n}\sum\limits_{i=1}^{\lfloor \frac{n}{d} \rfloor}id\cdot\varphi(i)=\frac{n(n+1)(2n+1)}{6}-\sum\limits_{d=2}^{n}\sum\limits_{i=1}^{\lfloor \frac{n}{d} \rfloor}id\cdot\varphi(i)$

杜教筛板子
 int Du_sieve(int n, int pre_f[], map<int, int>& mp) {//f*g=h,求f的前缀和
    if (n < N) return pre_f[n];
    if (mp.count(n)) return mp[n];
    int res = n * (n + 1) / 2;//h=id前缀和
    for (int l = 2, r;l <= n;l = r + 1) {
        r = n;
        if (n / l) r = min(r, n / (n / l));
        res = (res - Du_sieve(n / l, pre_f, mp) * (r - l + 1));//(r-l+1)为g=1前缀和
    }
    return mp[n] = res;
}

 

min_25筛

$Min25$筛求解前缀和本质上是一种$DP$。一般用于求解积性函数前缀和。

默认$P$为质数集,$P_i$表示第$i$个质数,默认$p\in P$。

若函数$f(x)$满足:$f(x)$是积性函数,$f(p)$存在多项式表示或可以快速求值,$f(p^k)$可以快速求值。

 

核心思想:将$[1,n]$分为质数,合数,$1$三个部分进行讨论。

$\sum\limits_{i=1}^{n}f(i)=\sum\limits_{i=1}^{n}[i\in P]f(i)+\sum\limits_{i=1}^{n}[i\notin P]f(i)$

$=\sum\limits_{i=1}^{n}[i\in P]f(i)+f(1)+\sum\limits_{2\le p\le \sqrt n,2\le p^e\le n}f(p^e)\left(\sum\limits_{i=2}^{\lfloor\frac{n}{p^e} \rfloor}f(i)[minp(i)>p]\right)$

 

首先考虑如何求解$\sum\limits_{i=1}^{n}[i\in P]f(i)$,考虑先线性筛出不大于$\sqrt n$的所有质数作为$P$。记$minp(x)$表示$x$的最小质因子,令$g(n,j)=\sum\limits_{i=1}^{n}[i\in P \; or \; minp(i)>P_j] i^k$,它表示$[1,n]$所有满足条件的数的$k$次方和,条件是要么是质数要么最小质因子大于$P_j$。这里选取$i^k$是因为它是一个完全积性函数,方便后面的公式推导。

可以发现$g(n,|P|)=\sum\limits_{i=1}^{n}[i\in P]i^k$,即$[1,n]$中所有质数的$k$次方和。为方便不妨令$g(n)=g(n,|P|)$。其中$|P|$表示$P$的大小,也就是不大于$\sqrt n$的质数数量。

考虑如何状态转移,$g(n,j)$在$g(n,j-1)$的基础上少了最小质因子恰好为$P_j$的合数。提出来一个$P_j$作为最小质因子,这样剩下的数也不能有小于它的质因子。也就是$g(\lfloor\frac{n}{P_j} \rfloor,j-1)-\sum\limits_{i=1}^{j-1}P_i^k$,后面这个是为了去掉前面满足条件的质数。这样我们可以得到递推式:$g(n,j)=g(n,j-1)- P_j^k\left[g(\lfloor\frac{n}{P_j} \rfloor,j-1)-\sum\limits_{i=1}^{j-1}P_i^k\right]$,式子中$\sum\limits_{i=1}^{j-1}P_i^k$,可以在筛素数的时候一并处理。

 

答案就是先求出所有质数的函数和,然后先枚举一个$p^e$,再枚举最小质因子大于$p$的数。

还是可以考虑$DP$的思想。设$S(n,x)$表示$[1,n]$中所有最小质因子大于$P_x$的函数值$f$之和。答案就是$S(n,0)$。

我们将满足条件的数分成两部分,第一部分是大于$P_x$的质数,即$g(n)-\sum\limits_{i=1}^{x}P_i^k$。另一部分则是最小质因子大于$P_x$的合数,枚举最小质因子即可。

于是$S(n,x)=g(n)-\sum\limits_{i=1}^{x}P_i^k+\sum\limits_{p_k^e\le n ,k>x }f(p_k^e)\left(S\left(\lfloor\frac{n}{p_k^e}\rfloor,k\right)+[e\ne1]\right)$

查看代码
 namespace min25 {
        const int SIZE = 1e5 + 10;
        const int EXP_SIZE = 2;
        int primes[SIZE], minFac[SIZE], pfx[SIZE][EXP_SIZE], primesPt, lim;
        ll g[SIZE * EXP_SIZE][EXP_SIZE], dsc[SIZE * EXP_SIZE], inv2, inv3;
        array<int, 2> indx[SIZE];  // indx[x]: index of <x, n / x>
        const array<int, 2> csts[EXP_SIZE] = { {-1, 1}, {1, 2} };

        void initPrimes(int siz) {
            inv2 = qp(2, MOD - 2, MOD); inv3 = qp(3, MOD - 2, MOD);
            fill(minFac + 0, minFac + siz + 1, 0); primesPt = 0;
            for (int i = 2; i <= siz; i++) {
                if (minFac[i] == 0) minFac[i] = i, primes[++primesPt] = i;
                for (int j = 1; j <= primesPt && primes[j] <= min(minFac[i], siz / i); j++) minFac[i * primes[j]] = primes[j];
            }

            //f(p)的多项式每一项的前缀和
            for (int i = 1; i <= primesPt; i++) {
                for (int e = 0; e < EXP_SIZE; e++) {//展开写可能变快
                    pfx[i][e] = Add(pfx[i - 1][e], qp(primes[i], csts[e][1], MOD));
                }
            }
        }

        //计算f(p)
        const auto f = [](ll p) {
            p %= MOD;
            return p * (p - 1) % MOD;
            //ll res = 0;
            // for (int e = 0; e < EXP_SIZE; e++) {//展开写会快很多
            //     res = (res + csts[e][0] * qp(p, csts[e][1], MOD)) % MOD;
            // }
            // return res;
            };

        //i^k前缀和,次数高的话需要拉插
        const auto sum = [](ll n, ll exp) {
            n %= MOD;
            if (exp == 0) return n;
            ll res = n * (n + 1) % MOD * inv2 % MOD;
            if (exp == 2) return res * ((n << 1) + 1) % MOD * inv3 % MOD;
            return res;
            };

        ll sieve(ll x, ll pt, ll n) {
            if (x <= 1 || primes[pt] > x) return 0;
            int k = x <= lim ? indx[x][0] : indx[n / x][1];
            ll res = 0;
            // for (int e = 0; e < EXP_SIZE; e++) {//展开写会快很多
            //     res = (res + csts[e][0] * (g[k][e] - pfx[pt - 1][e]) % MOD + MOD) % MOD;
            // }
            res = Add(res, Dec(pfx[pt - 1][0], g[k][0]));
            res = Add(res, Dec(g[k][1], pfx[pt - 1][1]));

            for (int i = pt; i <= primesPt && 1ll * primes[i] * primes[i] <= x; i++) {
                ll pk = primes[i], pk1 = 1ll * primes[i] * primes[i];
                for (int e = 1; pk1 <= x; pk = pk1, pk1 *= primes[i], e++) {
                    res = (res + f(pk) * sieve(x / pk, i + 1, n) % MOD + f(pk1)) % MOD;
                }
            }

            return (res + MOD) % MOD;
        }

        ll run(ll n) {
            lim = sqrt(n); initPrimes(lim); int dscPt = 0;
            for (ll l = 1, r; l <= n; l = r + 1) {
                r = n / (n / l); ll v = n / l; dsc[dscPt] = v;
                for (int e = 0; e < EXP_SIZE; e++)
                    g[dscPt][e] = sum(dsc[dscPt], csts[e][1]) - 1;
                v <= lim ? indx[v][0] = dscPt : indx[n / v][1] = dscPt; dscPt++;
            }

            for (int i = 1; i <= primesPt; i++) {
                for (int j = 0; j < dscPt && 1ll * primes[i] * primes[i] <= dsc[j]; j++) {
                    ll v = dsc[j] / primes[i];
                    int k = v <= lim ? indx[v][0] : indx[n / v][1];

                    // for (int e = 0; e < EXP_SIZE; e++) {//展开写会快很多
                    //     g[j][e] = (g[j][e] - qp(primes[i], csts[e][1], MOD) * (g[k][e] - pfx[i - 1][e] + MOD) % MOD + MOD) % MOD;
                    // }

                    g[j][0] = Dec(g[j][0], mul(primes[i], Dec(g[k][0], pfx[i - 1][0])));
                    g[j][1] = Dec(g[j][1], mul(mul(primes[i], primes[i]), Dec(g[k][1], pfx[i - 1][1])));

                }
            }

            return Add(sieve(n, 1, n), 1);
        }

 

整除分块

前置

给定$b,c$ ,求$a$ 。

$a\cdot b \le c$,则有$a\le \lfloor \frac{c}{b} \rfloor$

$a\cdot b < c$,则有$a< \lceil \frac{c}{b} \rceil$  ,也就是$a\cdot b\le c-1$,$a \le \lfloor\frac{c-1}{b} \rfloor$

$a\cdot b \ge c$,则有$a\ge \lceil \frac{c}{b} \rceil$

$a\cdot b > c$,则有$a> \lfloor \frac{c}{b} \rfloor$  ,也就是$a\cdot b\ge c+1$,$a \ge \lceil\frac{c+1}{b} \rceil$

 

对于$\lfloor\frac{n}{i}\rfloor$类似的式子,对于一个左边界$l$,其值为$k=\lfloor\frac{n}{l}\rfloor$,右边界$r$即找满足$k\le \lfloor\frac{n}{i}\rfloor$的最大的$i$ ,也就是使得$ik\le n$最大的$i$,即$r=\lfloor\frac{n}{k}\rfloor$,即$r= \left\lfloor \frac{n}{\lfloor\frac{n}{l} \rfloor} \right \rfloor$

查看代码
int lim = n;
for (int l = 1, r;l <= lim;l = r + 1) {
    r = lim;
    if (n / l) r = min(r, n / (n / l));
    //操作
}

拓展

 

我对整除分块的理解:

首先用$\lfloor\frac{n}{l}\rfloor$得到$k$,然后$\frac{n}{l}$应该为$k.几$,也就是$k+\varepsilon_1$,其中$\varepsilon_1\in[0,1)$

接下来用$\frac{n}{k}$,如果是$\frac{n}{k+\varepsilon_1}$,就会得到$l$,然而去掉了$\varepsilon_1$,$k$减小了,于是得到的值就变大,得到了$r+\varepsilon_2$。其中$\varepsilon_2\in[0,1)$

于是可以得出的是,在$x\in[l,r+\varepsilon_2]$范围,如果用$\frac{n}{x}$都将得到$[k,k+\varepsilon_1]$范围的值,其中$x=r+\varepsilon_2$时,$\frac{n}{x}=k$;$x=l$时,$\frac{n}{x}=k+\varepsilon_1$。于是取个整,$x\in[l,r]$的范围,$\lfloor\frac{n}{x}\rfloor=k$

下面的都可以这样理解。

 

给定$n$,求$\lfloor\frac{n}{ai+b}\rfloor$的整除分块

先令$i'=ai+b$,那么$l'=al+b$,$r'=ar+b$。 令$k=\lfloor \frac{n}{l'} \rfloor$,$r'$是最大的$i'$使得$ki'\le n$,那么$r'=\lfloor \frac{n}{k} \rfloor$

带入$l'=al+b$,得到$k=\lfloor \frac{n}{al+b} \rfloor$ ,$r'=\left\lfloor \frac{n}{\lfloor \frac{n}{al+b} \rfloor} \right\rfloor$

$r'=ar+b=\left\lfloor \frac{n}{\lfloor \frac{n}{al+b} \rfloor} \right\rfloor$ ,那么$r=\left\lfloor\frac{\left\lfloor \frac{n}{\lfloor \frac{n}{al+b} \rfloor} \right\rfloor-b}{a}\right\rfloor$

给定$n$,求$\lfloor\frac{n}{i^2}\rfloor$的整除分块

令$i'=i^2$,那么$l'=l^2$,$r'=r^2$。令$k=\lfloor\frac{n}{l'}\rfloor$,$r'$是最大的$i'$使得$ki'\le n$,那么$r'=\lfloor\frac{n}{k}\rfloor=\left\lfloor\frac{n}{\lfloor\frac{n}{l^2}\rfloor}\right\rfloor$

$r'=r^2=\left\lfloor\frac{n}{\lfloor\frac{n}{l^2}\rfloor}\right\rfloor$ ,得到$r=\left\lfloor\sqrt{\left\lfloor\frac{n}{\lfloor\frac{n}{l^2}\rfloor}\right\rfloor}\right\rfloor$

给定$n$,求$\lceil\frac{n}{i}\rceil$的整除分块

由于$\lceil\frac{n}{i}\rceil=\lfloor\frac{n+i-1}{i}\rfloor$ ,令$k=\lfloor\frac{n+l-1}{l}\rfloor$,$r$是最大的$i$使得$ik\le n+i-1$,那么$r=\lfloor\frac{n-1}{k-1} \rfloor=\left\lfloor\frac{n-1}{\lfloor\frac{n+l-1}{l}\rfloor-1}\right \rfloor$

 

$log$分块

$\sum\limits_{i=1}^{n}\lfloor log_ai \rfloor$

查看代码
for (int l = 1, r, k = (int)(log(l) / log(a));l <= n;l = r + 1, k++) {
    r = min(l * a - 1, n);
    res += k * (r - l + 1);
}

 

 

积性函数大赏

积性函数:一个定义域为正整数$n$的算术函数$f(n)$,有如下性质:$f(1)=1$,且当$a,b$互质时,$f(ab)=f(a)f(b)$。如果一个数字$n=p_1^{c_1} p_2^{c_2} \cdots p_k^{c_k}$,那么$f(n)=f(p_1^{c_1})f(p_2^{c_2}) \cdots f(p_k^{c_k})$,那么研究一个积性函数$f$可以转化为研究$f(p^c)$

完全积性函数:$f(1)=1$,且对任意两个正整数$a,b$都有$f(ab)=f(a)f(b)$

性质1:两个积性函数的狄利克雷卷积必定是积性函数。

性质2:狄利克雷卷积满足交换律,结合律,分配律。交换律即$f*g=g*f$,也就是$\sum\limits_{d|n}f(d)g(\frac{n}{d})=\sum\limits_{d|n}f(\frac{n}{d})g(d)$。分配律即$(f+g)*h=f*h+g*h$。结合律即$(f*g)*h=f*(g*h)$

1.

单位函数:$\varepsilon(n)=[n=1]$ (完全积性)

2.

幂函数:$id_k(n)=n^k$  (完全积性)

3.

常数函数:幂函数中,$k=0$时, $id_0(n)=1(n)=1$ ,或作$I(n)=1$ (完全积性)

4.

恒等函数:幂函数中,$k=1$时, $id_1(n)=id(n)=n$ (完全积性)

5.

除数函数:$\sigma_k(n)$,$n$所有正因数的$k$次幂之和。 这里$k$可以是任何复数和实数。

求解:

先来看$k=1$的情况,也就是约数和。

$(1+p_1+...+p_1^{c_1})...(1+p_n+...+p_n^{c_n})=\prod\limits_{i=1}^{n}(\sum\limits_{j=0}^{c_j}p_i^j)$

每个括号里面就是每一个质数的任一个次幂,就是每个括号里面选一个数字乘起来,再把所有选择加起来。显然这就是约束之和。

那么$k$次幂,显然就是$\prod\limits_{i=1}^{n}(\sum\limits_{j=0}^{c_j}(p_i^j)^k)=\prod\limits_{i=1}^{n}(\sum\limits_{j=0}^{c_j}(p_i^k)^j)$,显然可以用等比数列求和。

例题

用PR分解longlong范围质因子,然后用上式计算。

查看代码
 void Solve(int TIME) {

    int n, k;cin >> n >> k;
    Prime_factor(n);
    map<int, int> mp;
    for (auto i : res) {
        while (n % i == 0) mp[i]++, n /= i;
    }
    int res = 1;
    for (auto [i, j] : mp) {
        int p = i;p = qp(p, k);
        res = res * (qp(p, j + 1) - 1) % MOD * inv(p - 1) % MOD;
    }
    cout << res << endl;

}

6.

因数个数函数:除数函数中,$k=0$时,$\sigma_o(n)=d(n)=\prod\limits_{i=1}^{k}(c_i+1)$ ,$n$的正因数个数,$n=p_1^{c_1}p_2^{c_2}\cdots p_k^{c_k}$

7.

因数和函数:除数函数中,$k=1$时,$\sigma_1(n)=\sigma(n)=\prod\limits_{i=1}^{k}\sum\limits_{j=0}^{c_i}p_i^j$ ,$n$的所有正因数之和,$n=p_1^{c_1}p_2^{c_2}\cdots p_k^{c_k}$

8.

莫比乌斯函数:$\mu(n)=\begin {cases} 1,n=1\\0,n含有平方因子\\(-1)^k ,n=\prod\limits_{i=1}^{k}p_i,p_i为质数 \end{cases} $

性质:$\sum\limits_{d|n}\mu(d)=\varepsilon(n)$ 即$\mu*1=\varepsilon$ 。这意味着在狄利克雷卷积中,$\mu$是常数函数$1$的逆元。

证明:设$n=\prod\limits_{i=1}^{k}p_i^{c_i}$,$n'=\prod\limits_{i=1}^{k}p_i$ ,那么$\sum\limits_{d|n}\mu(d)=\sum\limits_{d|n'}\mu(d)=\sum\limits_{i=0}^{k}C_{k}^{i}(-1)^i=(1+(-1))^k=[k=0]=[n=1]$

扩展:$[gcd(i,j)=1]=\sum\limits_{d|gcd(i,j)}\mu(d)$ ,将$n$直接用$gcd(i,j)$替换即可

9.

欧拉函数:$\varphi(n)=\sum\limits_{i=1}^{n}[gcd(i,n)=1]$ ,表示$[1,n]$中与$n$互质的数的个数

计算公式: $n=\prod\limits_{i=1}^{k}p_i^{c_i}$,$p_i$为质数 。 $\varphi(n)=n(1-\frac1{p_1})(1-\frac1{p_2})\cdots(1-\frac1{p_k})$

感性的证明:以$12$举例,$12$有两个质因子$2,3$。在$[1,12]$中,有$\frac1{2}$的数是$2$的倍数,所以有$1-\frac1{2}$是数不是$2$的倍数,在这不是$2$的倍数的数中,有$\frac{1}{3}$的数是3的倍数,所以既不是$2$的倍数也不是$3$的倍数的数字有$12(1-\frac1{2})(1-\frac{1}{3})$ 。

性质1:若$p$是质数,$\varphi(p)=p-1$ 。

性质2:若$p$是质数,$n=p^k$,$\varphi(n)=p^k-p^{k-1}$ 。证明:$\varphi(n)=n(1-\frac{1}{p})=p^k(1-\frac1{p})=p^k-p^{k-1}$

性质3:$\sum\limits_{d|n} \varphi(d)=n $即  $\varphi*1=id$ 。

证明:$n=\prod\limits_{i=1}^{k}p_i^{c_i}$,由于$\varphi$为积性函数,故只需要证明当$n'=p^c$时,$\varphi*1=\sum\limits_{d|n'}\varphi(d)1(\frac{n'}{d}) = id$

显然$d=p^0,p^1,p^2,...,p^c$ ,那么$\sum\limits_{i=0}^{c}\varphi(p^i)=\sum\limits_{i=0}^{c}p^i(1-\frac{1}{p})=1+(1-\frac{1}{p})\sum\limits_{i=1}^cp^i=1+(1-\frac{1}{p})\frac{p(1-p^c)}{1-p}=p^c=id(n')$

 

狄利克雷函数与欧拉函数的联系:对$\varphi*1=id$两边同时卷上$\mu$,得到$\varphi*1*\mu=id*\mu$

由于狄利克雷卷积满足结合律,$\varphi*\varepsilon=id*\mu$即$\varphi=\mu*id$

 

比较有用的式子

根据定义 $\varphi(n)=\sum\limits_{i=1}^{n}[gcd(i,n)=1]=\sum\limits_{i=1}^{n-1}[gcd(i,n)=1]+\varepsilon(n)$

$\sum\limits_{i=1}^{n}\varphi(i)=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{i-1}[gcd(i,j)=1]+1$,即有序对的贡献再加上$1$ 。

那么$\sum\limits_{i=1}^{n}\varphi(i)=\frac12\left(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[gcd(i,j)=1]-\sum\limits_{i=1}^{n}[gcd(i,i)=1]\right)+1=\frac12\left(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[gcd(i,j)=1]+1\right)$

即$\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[gcd(i,j)=1]=2\sum\limits_{i=1}^{n}\varphi(i)-1$

 

10.

$\omega(n)$表示$n$的质因子数目(不可重复)  ,例如$n=p_1^{c_1} p_2^{c_2} \cdots p_k^{c_k}$ ,$\omega(n)=k$

11.

$\Omega(n)$表示$n$的质因子数目(可重复) ,例如$n=p_1^{c_1} p_2^{c_2} \cdots p_k^{c_k}$ ,$\Omega(n)=\sum\limits_{i=1}^{k}c_i$

12.

刘维尔函数:$\lambda(n)=(-1)^{\Omega(n)}$

性质:$\sum\limits_{d|n}\lambda(d)=\begin{cases} 1,若n是平方数\\0,若n不是平方数 \end{cases}$ ,$\lambda(n)=\sum\limits_{d^2|n}\mu(\frac{n}{d^2})$

13.

$\gamma(n)=(-1)^{\omega(n)}$

14.

最大公约数:$gcd(n,k)$当$k$固定时,是积性函数

 

莫比乌斯反演

因数形式

$f(n)=\sum\limits_{d|n}g(d) ⟺  g(n)=\sum\limits_{d|n}\mu(d)f(\frac{n}{d})$  

即$f=g*1 ⟺ g=\mu*f$

证明:

两边同时乘以$\mu$得到$f*\mu=g*1*\mu=g*\varepsilon=g$

莫比乌斯容斥

跟莫比乌斯反演关系不大,但是因为很有用并且用到了莫比乌斯函数,所以也放在这里

从埃筛到莫比乌斯容斥

用$cnt_i$表示$1\sim n$中,$i$出现的次数,显然$cnt_i=1$

用$k_i$表示$1\sim n$中,$i$的倍数出现的次数。

如何用$k$来表示出$cnt$呢?

这里不妨设$n=10$。

1 2 3 4 5 6 7 8 9 10
1 1 1 1 1 1 1 1 1 1
 -1  -1  -1  -1  -1
   -1    -1    -1
       -1        -1
          1
           -1
                  1

$cnt_1=k_1-k_2-k_3-k_5+k_6-k_7+k_{10}$,首先筛$1$的倍数,给每个$1$的倍数都加$1$,然后筛$2$的倍数,给每个$2$的倍数减去$1$,然后筛$3$的倍数,也给每个$3$的倍数都减去$1$。然后发现$4$已经被筛过了,跳过。然后是$5$的倍数,也都减去$1$。接下来是$6$的倍数,发现它被重复减了一次,于是枚举$6$的倍数,加回来一次。接下来筛$7$的倍数,给每个都减去$1$。接下来发现$8$的倍数已经筛过了,于是跳过。然后$9$也筛过了,跳过。最后$10$的倍数,发现多减了一次,加回来。

找一下有什么规律?$2$,$3$,$5$,$7$都包含了奇数个质因子所以是减去,$6$和$10$则包含了偶数个质因子所以是加上,而$4$,$8$,$9$包含了相同的质因子多次,也就是有平方数因数。接下来特殊定义$1$的时候是加上。

这个时候发现它恰好满足莫比乌斯函数的定义。

多推点式子看看:$cnt_2=k_2-k_4-k_6-k_{10}=k_{2\cdot 1}-k_{2\cdot 2}-k_{2\cdot 3}-k_{2\cdot 5}$

于是得到一个通式:$cnt_x=\sum\limits_{i=1}^{\lfloor\frac{n}{x}\rfloor}k_{x\cdot i}\mu_i$

 

做些推广就得到了莫比乌斯容斥:

设$f(x)$表示$1\sim n$中$x$出现的次数($x$的贡献)

$g(x)$表示$1\sim n$中有多少个是$x$的倍数($x$的倍数的贡献)

则有$f(x)=\sum\limits_{i=1}^{\lfloor \frac{n}{x} \rfloor}g(i\cdot x)\mu(i)$

 

例如:

对于$g(x)=\sum\limits_{i=1}^n\sum\limits_{j=1}^{n} [gcd(a_i,a_j)=x]$的形式,可以令$f(d)=\sum\limits_{x=d,2d,...}g(x)=\sum\limits_{i=1}^n\sum\limits_{j=1}^{n}][d|gcd(a_i,a_j)]=\sum\limits_{i=1}^n\sum\limits_{j=1}^{n}[d|a_i][d|a_j]=(\sum\limits_{i=1}^{n}[d|a_i])^2$  

$f(x)$可以$O(nlogn)$时间复杂度求出。

于是就有$g(d)=\sum\limits_{x=d,2d,...} f(x)\mu(\frac{x}{d})$

即$g(x)=\sum\limits_{n=x,2x,...} f(n)\mu(\frac{n}{x})=\sum\limits_{n=x,2x,...}(\sum\limits_{i=1}^{n}[n|a_i])^2\mu(\frac{n}{x})$

也可以$O(nlogn)$复杂度求出。

 

 

欧拉反演

利用$id=\varphi*1$,即$F(x)=\sum\limits_{d|F(x)}\varphi(d)$

例如:

对于$f(x)=\sum\limits_{i=1}^n\sum\limits_{j=1}^{n}gcd(a_i,a_j)$的形式,可以得到$f(x)=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\sum\limits_{d|gcd(a_i,a_j)} \varphi(d)=\sum\limits_{i=1}^{n}[d|a_i]\sum\limits_{j=1}^{n}[d|a_j]\sum\limits_{d=1}^{n} \varphi(d)$

 

矩阵树定理

$Laplacian$矩阵

$g(i,i)$表示$i$的度数,入度+出度。

如果$i$和$j$ $(i\neq j)$之间有边的话,就给对应的位置$g(i,j)$填上$-1$,否则为$0$。如果有重边,那么就改成对应的负几即可。

无向图生成树个数

$Laplacian$矩阵任选一个$i$后,去掉$i$行$i$列的矩阵的行列式。

生成树权值

边权的乘积。那么可以用有重边的生成树来表示所有生成树的权值之和。这个很容易理解,对于重边的图,生成树个数显然是每个边数乘起来后再乘上去重后的生成树个数。

有向图的生成树

以$r$为根的生成树,并且$r$能到达所有点。

有向图的生成树个数

以$r$为根的生成树的个数是,在$Laplacian$矩阵去掉$r$行$r$列的矩阵的行列式。

会用到的板子

辗转相除求行列式
 int g[N][N];

int det(int n) {
    int ans = 1;
    for (int i = 1;i <= n;i++) {
        for (int j = i + 1;j <= n;j++) {
            //消掉g[j][i]
            int x = i, y = j;
            while (g[x][i]) {
                int t = g[y][i] / g[x][i];
                for (int k = i;k <= n;k++) {
                    g[y][k] = (g[y][k] - t * g[x][k]) % MOD;
                }
                swap(x, y);
            }
            //g[x][i]=0
            if (x == i) {
                for (int k = i;k <= n;k++) {
                    swap(g[i][k], g[j][k]);
                }
                ans = -ans;
            }
        }
        if (g[i][i] == 0){
            return 0;
        }
        ans = ans * g[i][i] % MOD;
    }
    if (ans < 0) ans += MOD;
    return ans;
}

void Solve(int TIME) {

    int n, m;cin >> n >> m;
    for (int i = 1;i <= m;i++) {
        int u, v;cin >> u >> v;
        g[u][v]--, g[v][u]--;
        g[u][u]++, g[v][v]++;
    }

}

 

超几何分布

$n$个物品,其中有$m$个次品。不放回的取$k$个物品,有$x$个次品的概率为$\frac{C_{m}^{x}C_{n-m}^{k-x}}{C_{n}^{k}}$。 从组合的角度很容易理解

01分数规划

给定两个数组,$a_i$表示选取$i$的收益,$b_i$表示选取$i$的代价,定义$x[i]=1$或$0$,表示选或不选,每种物品只有选或不选两种方案,求出$\frac{\sum\limits_{i=1}^{n}a_i x_i}{\sum\limits_{i=1}^{n}b_i x_i }$的最大值/最小值

二分

假设我们求最大值,二分一个答案$mid$,当存在一组$x_i$,满足$\frac{\sum\limits_{i=1}^{n}a_i x_i}{\sum\limits_{i=1}^{n}b_i x_i }>mid$,则说明当前的$mid$可以。

$\frac{\sum\limits_{i=1}^{n}a_i x_i}{\sum\limits_{i=1}^{n}b_i x_i }>mid \;⇒\; \sum\limits_{i=1}^{n}a_ix_i-mid\sum\limits_{i=1}^{n}b_ix_i>0 \;⇒\;\sum\limits_{i=1}^{n}x_i(a_i-mid \cdot b_i)>0$

只要求出上述式子的最大值就可以了,如果最大值比$0$大,说明$mid$是可行的,否则不可行。

如果要求最小值,只要比$0$小即可。

二分求最小值
 const ld eps = 1e-8;
ld l = eps, r = 1e9;
while (r - l > eps) {
    ld mid = (l + r) / 2;
    vc<ld> g;
    for (auto [x, y, a, b] : edge) {
        g.push_back(a - b * mid);
    }
    sort(g.begin(), g.end());
    ld res = 0;
    for (int i = 0;i < k;i++) {//选k个物品
        res += g[i];
    }
    if (res < eps) r = mid;//ans
    else l = mid;
}
二分求最大值
 const ld eps = 1e-8;
ld l = eps, r = 1e9;
while (r - l > eps) {
    ld mid = (l + r) / 2;
    vc<ld> g;
    for (auto [x, y, a, b] : edge) {
        g.push_back(-(a - b * mid));
    }
    sort(g.begin(), g.end());
    ld res = 0;
    for (int i = 0;i < k;i++) {//取k个物品
        res += g[i];
    }
    if (res > eps) l = mid;//ans
    else r = mid;
}

$Dinkelbach$算法

思想是每次用上一轮的答案当做新的$mid$来输入,不断地迭代,直至答案满足精度。比二分更快。

Dinkelbach求最小值
 const ld eps = 1e-8;
ld ans = 1e9;
while (1) {
    DSU d(n + 1);
    vc<pair<ld, ai2>> g;
    for (auto [x, y, a, b] : edge) {
        g.push_back({ a - b * ans,{a,b} });
    }
    sort(g.begin(), g.end());
    for (int i = 0;i < k;i++)//取k个物品
        A += g[i].second.at(0), B += g[i].second.at[1];
    ld t = 1.0 * A / B;
    if (abs(t - ans) < eps) break;
    ans = t;
}
Dinkelbach求最大值
 const ld eps = 1e-8;
ld ans = eps;
while (1) {
    DSU d(n + 1);
    vc<pair<ld, ai2>> g;
    for (auto [x, y, a, b] : edge) {
        g.push_back({ -(a - b * ans),{a,b} });
    }
    sort(g.begin(), g.end());
    for (int i = 0;i < k;i++)//取k个物品
        A += g[i].second.at(0), B += g[i].second.at[1];
    ld t = 1.0 * A / B;
    if (abs(t - ans) < eps) break;
    ans = t;
}


求二元一次不定方程

给定$a,b,c$ ,求$ax+by=c$中$x$和$y$的整数解。

首先用exgcd求出一组特解$x_0,y_0$,然后有$ax_0+by_0=gcd(a,b)=d$,如果$c \;mod\;d \neq 0$则无解。

令$base=\frac{c}{d}$,则有$a(x_0\cdot base+\frac{b}{gcd(a,b)})+b(y_0\cdot base-\frac{a}{gcd(a,b)})=c$ 。则有通解为$\begin{cases} x=x_0 \cdot \frac{c}{d} +k\cdot\frac{b}{gcd(a,b)}\\y=y_0\cdot \frac{c}{d}-k\cdot\frac{a}{gcd(a,b)} \end{cases}$ 。则很显然只要对$x_0\cdot \frac{c}{d}$关于$\frac{b}{gcd(a,b)}$取模就能得到$x$的最小非负整数解,对$y$同理,只要对$y_0\cdot \frac{c}{d}$关于$\frac{a}{gcd(a,b)}$取模就能得到$y$的最小非负整数解

模板题:P5656 二元一次不定方程

如果是非负整数,则是完全背包和同余最短路相关。具体见图论。

 

$Lucas$定理

求$C_n^m \;mod\; P$,$P$为质数

将$n$写成$P$进制数,获得$n_0P^0+n_1P^1+n_2P^2+...$

将$m$写成$P$进制数,获得$m_0P^0+m_1P^1+m_2P^2+...$

则$C_n^m \;mod\; P=C_{n_0}^{m_0}\cdot C_{n_1}^{m_1}\cdot C_{n_2}^{m_2}... \;mod\;P$

 

阶,原根,指数方程

:满足$a^x \equiv 1(\mod m)$ 最小的正整数$x$,读作$a$模$m$的阶。当前仅当$gcd(a,m)=1$这个方程会有解。

由欧拉公式我们知道,当$\gcd(a,m)=1$,一定有$a^{\varphi(m)}=1 (\mod m)$

阶可以理解为,从$1$开始不停的乘$a(\mod m)$,一个循环的长度。

那么如果$a^x\equiv 1(\mod m)$,那么一定有$阶|x$ 。

因此我们知道,一定有$阶|\varphi(m)$,也就是说如果要回到循环原点,步数一定是环长的倍数。

于是过程是这样的,将$\varphi(m)$用唯一分解定理表示成$\varphi(m)=p_1^{e_1}p_2^{e_2}...p_k^{e_k}$,对于每个质因子,如果去掉一个之后还是阶的倍数,即$a^{\frac{x}{p_i}}=1(\mod m)$,那么就去掉。

 

原根:$g\mod m$的阶为$\varphi(m)$,那么$g$为$m$的原根。

结论:只有$1$,$2$,$4$,$p^a$,$2p^a$存在原根,这里的$p$为奇素数。

原根不是唯一的。最小的原根不会很大。原根的个数为$\varphi(p-1)$个。

从小到大枚举或随机$g$,然后判断是否是原根。跟上面差不多,分解为若干质数$p_i$后,判断每个$p_i$是否都有$g^{\frac{\varphi(m)}{p_i}} \neq 1(\mod a)$即可。

需要注意的是,$p$的原根不一定是$p^2$的原根

当$g$为原根时,由于$1$到$m$之间与$m$互质的数有$\varphi(m)$个, 又因为原根的性质得到循环节长度是$\varphi(m)$,又知道循环节里面每个数之间都是互不相同的。所以这里面的任意一个数都可以用$g^i$表示。

 

指数方程:求$a^x \equiv b(\mod m)$,这里$\gcd(a,m)=1$

由欧拉定理,我们知道这个方程如果有解,答案一定是在$0$到$\varphi(m)-1$之间,因此最暴力的做法就是for循环累乘,但是这样复杂度就是比较高的。

这里运用meet in the middle的思想,令$T=\lceil \sqrt{m} \rceil$,那么$0$到$m-1$的数字一定可以表示成$qT+r$的形式,其中$q\in[0,T-1]$,$r\in [0,T-1]$。那么这个方程可以表示成$a^{qT+r}\equiv b(\mod m)$。

移项可得$(a^T)^q \equiv b\cdot a^{-r} (\mod m)$。就相当于在两个数组$a^{T\cdot 0},a^{T\cdot 1},...,a^{T\cdot (T-1)}$和$b\cdot a^{-0},b\cdot a^{-1},...,b\cdot a^{-(T-1)}$里面找是否存在两个相同的数字。

 

代码中,为了方便起见,用$x=qT-r$表示,其中$q\in[1,T]$,$r\in [1,T]$

那么就会变成$a^{qT} \equiv b \cdot a^r $,这样这个$r$就不需要求逆元,方便一点。

查看代码
 int bsgs(int a, int b, int mod) {
    a %= mod, b %= mod;
    int T = sqrt(mod) + 2;
    int v = qp(a, T, mod), d = 1;
    unordered_map<int, int> hs;
    for (int q = 1;q <= T;q++) {
        d = d * v % mod;
        if (!hs.count(d)) hs[d] = q * T;
    }

    int ans = mod + 1;
    d = b;
    for (int r = 1;r <= T;r++) {
        d = d * a % mod;
        if (hs.count(d)) ans = min(ans, hs[d] - r);
    }
    if (ans >= mod) return -1;
    else return ans;

}

 

容斥原理

容斥原理的基本形式

$|S-\bigcup\limits_{i=1}^{n}A_i|=\sum\limits_{i=0}^{n}(-1)^{i}\sum\limits_{1\le j_1 \le j_2 \le...\le j_i \le n} |\bigcap\limits_{k=1}^{i}A_{j_k}|$

 

容斥原理的符号形式

设$S$是一个有限集,$a_1,...,a_n$是$n$种性质。

记$N(a_i)$为$S$中有$S$中有$a_i$性质的元素的数量。特殊的,记$N(1)=|S|$。

记$N(1-a_i)$为$S$中没有$a_i$性质的元素的数量。

$N(a_{i_1}a_{i_2}...a_{i_k})$为$S$中同时有$a_{i1},a_{i2},...,a_{i_k}$性质的元素数量。

记$N(a\pm b)=N(a) \pm N(b)$

那么容斥原理可以写成 

$N((1-a_1)(1-a_2)...(1-a_n))=\sum\limits_{i=0}^{n}(-1)^i \sum\limits_{1\le j_1 <j_2<...<j_i \le n}N(a_{j_1}a_{j_2}...a_{j_i})$

$N(a_1...a_x(1-a_{x+1})...(1-a_{x+n}))=\sum\limits_{i=0}^{n}(-1)^i\sum\limits_{x \le j_1 <j_2<...<j_i \le x+n} N(a_1...a_x a_{j_1}...a_{j_i})$

 

而交集可以表示为$N(a_1a_2...a_n)$,并集表示为$N(1-(1-a_1)(1-a_2)...(1-a_n))$表示至少有一个满足条件的补集。

 

表达成了代数形式,可以简化一些问题的推导过程。

 

例题1:求正整数$1$到$n$中,既不是2的倍数而不是3的倍数,但是是5的倍数的数的数量。

设$a_1:是2的倍数 ,a_2:是3的倍数,a_3:是5的倍数$

$N((1-a_1)(1-a_2)a_3)=N(a_3-a_1a_3-a_2a_3+a_1a_2a_3)$

$=N(a_3)-N(a_1a_3)-N(a_2a_3)+N(a_1a_2a_3)=\lfloor\frac{n}{5}\rfloor-\lfloor\frac{n}{10}\rfloor-\lfloor\frac{n}{15}\rfloor+\lfloor\frac{n}{30}\rfloor$

 

例题2:刚好存在$k$个$i$满足$p_i=i$的排列个数。

设$a_i: \; p_i=i$

$N(a_{1}...a_{k}(1-a_{k+1})...(1-a_n))=\sum\limits_{i=0}^{n-k}(-1)^iC_{n-k}^{i}N(a_1...a_ka_{j_i}...a_{j_i})$

$=\sum\limits_{i=0}^{n-k}(-1)^iC_{n-k}^{i}(n-(k+i))!$

又因为前面这$k$个是任取的,所以要总体再乘上$C_{n}^{k}$ 。答案就是$C_n^k\sum\limits_{i=0}^{n-k}(-1)^iC_{n-k}^{i}(n-(k+i))!$

 

这个形式可以窥见一些二项式反演的迹象,有时候使用比二项式反演还要方便,但有时候还是二项式反演方便。建议把两个方法都熟练掌握。

 

 

位运算常见结论

$a|b + a\&b =a+b$

$a|b-a\&b=a\text{^} b$

$a-b=2a-(a+b)=2a-a|b-a\&b$

可以从集合的角度画个图理解:两个集合的总和是两个集合的交集加上并集,两个集合的异或是两个集合的并集减去两个集合的交集。

 

裴蜀定理

$a,b$是不全为$0$的整数,对任意整数$x,y$,满足$gcd(a,b) \;|\; ax+by$,且存在整数$x,y$,使得$ax+by=gcd(a,b)$。

推论:在长为$n$的循环数组(即数组中的最后一个元素的下一个元素是数组中的第一个元素,数组中第一个元素的前一个元素是数组中的最后一个元素)中,如果有周期$n$和$k$($n$是循环数组自带的性质导致的),有$a_i=a_{i+nx+ky}=a_{i+gcd(n,k)}$ 。

 

勒让德定理

$n!$做质因子分解后,质因子$p$的次数为$\sum\limits_{k=1}\lfloor\frac{n}{p^k} \rfloor$。

证明:这$n$个数的连乘积中,只有$p、2p、...、\lfloor \frac{n}{p} \rfloor \cdot p$能被$p$整除。从而有了$\lfloor \frac{n}{p} \rfloor$个质因子$p$ 。只有$p^2、2p^2、...、\lfloor \frac{n}{p^2} \rfloor\cdot p^2$,考虑到每个能被$p^2$整除的数都被$p$整除,所以只需要加上$\lfloor\frac{n}{p^2}\rfloor$个即可,以此类推即是结果。

 

$[n,2n]$之间必然存在质数

证明很复杂,但是可以用哥德巴赫猜想做伪证明。显然$2n$是个偶数,那么右哥猜得到存在两个质数$p+q=2n$,如果$[n,2n]$不存在质数,那么$p<n,q<n$,不成立。

加强:伯特兰-切比雪夫定理,若整数$n>3$,则至少存在一个质数$p$,符合$p\in(n,2n-2)$。

 

线性规划

最大化目标函数:

需要最大化$\boldsymbol {C^{T}X} $,即$c_1x_1+...+c_mx_m$

其中$\boldsymbol X\ge 0$   $\boldsymbol {AX}\le \boldsymbol B$

其中$\boldsymbol A$是一个$n\times m$的矩阵,$\boldsymbol B$是一个$n$维向量,$\boldsymbol X$是一个$m$维向量。

$n$和$m$表示约束的个数和变量的个数。参数$\boldsymbol a$和$\boldsymbol b$表示线性规划问题的系数矩阵和约束向量。

 

 

线性代数

向量空间

向量空间是由向量组成的线性空间,定义$(F,V,+,\cdot)$​为向量空间,其中$F$​为域,$V$​为集合,$V$​中元素称为向量,$+$​为向量加法,$\cdot $​为标量乘法。

线性相关

对于向量空间中$V$上$n$个元素的向量组$(\boldsymbol{v}_{1},...,\boldsymbol{v}_{n})$,若存在不全为$0$的数$a_i\in F$,满足$a_1\boldsymbol v_1+a_2\boldsymbol v_2+...+a_n\boldsymbol v_n=0$,则称这$n$个向量线性相关,否则称为线性无关。

线性组合

对于向量空间中$V$上$n$个元素的向量组$(\boldsymbol{v}_{1},...,\boldsymbol{v}_{n})$,其线性组合是如下形式的向量,$a_1\boldsymbol v_1+a_2\boldsymbol v_2+...+a_n\boldsymbol v_n$,其中$a_1,...,a_n\in F$。一组向量线性无关$\Longleftrightarrow$没有向量可用有限个其他向量线性组合所表示

张成

对于向量空间中$V$上$n$个元素的向量组$(\boldsymbol{v}_{1},...,\boldsymbol{v}_{n})$,其所有线性组合所构成的集合称为$(\boldsymbol{v}_1,...,\boldsymbol{v}_{n})$的张成,记为$span(\boldsymbol{v}_{1},...,\boldsymbol{v}_{n})$。

若向量空间$V$中向量组$\mathcal B$既是线性无关的又可以张成$V$,则称其为$V$的基。

$\mathcal B$​中的元素称为基向量,如果基中的元素个数有限,就称向量空间为有限维向量空间,将元素的个数称作向量空间的维数。

性质:设$\mathcal B$是向量空间$V$的基,则$\mathcal B$具有以下性质:

1.$V$是$\mathcal B$的最小生成集,就是说只有$\mathcal B$能张成$V$,而它的任何真子集都不张成全部的向量空间。

2.$\mathcal B$是$V$中线性无关向量的极大集合,就是说$\mathcal B$在$V$中是线性无关集合,而且$V$中没有其他线性无关集合包含它作为真子集。

3.$V$中所有向量都可以按唯一的方式被表达为$\mathcal B$中向量的线性组合。

 

线性相关性引理

如果$(\boldsymbol v_1,...,\boldsymbol v_n)$在$V$中是线性相关的,并且$\boldsymbol v_1\ne 0$,则有至少一个$j\in \{2,...,n\}$使得下列成立:

1. $\boldsymbol v_j\in span(\boldsymbol v_1,...,\boldsymbol v_{j-1})$
2. 如果从$(\boldsymbol v_1,...,\boldsymbol v_n)$去掉第$j$项,则剩余向量组的张成仍然等于$span(\boldsymbol v_1,...,\boldsymbol v_n)$

 

下面介绍算法竞赛中的线性基

异或线性基

对于数$a_0,a_1,...,a_n$将$a_i$的二进制表示$(b_m...b_0)_2$看作一个向量$\boldsymbol a_i=(b_m,...,b_0)$。

下文称向量$\boldsymbol a_i$的第$j$位为$\boldsymbol a_{i,j}$。

向量组$\boldsymbol a_1,...,\boldsymbol a_n$可以张成一个向量集合$span(\boldsymbol a_1,...,\boldsymbol a_n)$,加上异或运算和乘法运算,即可形成一个向量空间$V=(\{0,1\},span(\boldsymbol a_1,...,\boldsymbol a_n),\text{^} ,\cdot )$。

性质:对于任意存在于线性基的二进制位$i$,至多只有一个$\boldsymbol a$满足第$i$位为$1$。

模拟这个过程,$n=5$,$a={7,1,4,3,5}$。

一开始矩阵是$\left[\begin{array}{l}   0&0&0 \\0&0&0\\0&0&0 \end{array}\right]$,

加入$7=(111)_2$,矩阵变成:$\left[\begin{array}{l}   1&1&1 \\0&0&0\\0&0&0 \end{array}\right]$

加入$1=(001)_2$,添加到最后一行,同时消去第一行的最低位,矩阵变成:$\left[\begin{array}{l}   1&1&0 \\0&0&0\\0&0&1 \end{array}\right]$,

加入$4=(100)_2$,由于第一行已经有数字,它被异或为$(010)_2$,加入第二行,同时消去第一行的第二位,矩阵变成:$\left[\begin{array}{l}   1&0&0 \\0&1&0\\0&0&1 \end{array}\right]$.

剩下的数字都加不上了。

 

威尔逊定理

$(p-1)! \equiv-1(mod \;p)$是$p$为质数的充分必要条件。

验证充分性:

非质数的情况,带入$1$发现结果为$0$,带入$4$结果为$2$。

当$p>4$时,如果$p$是完全平方数,那么$p=k^2$,由于$p>4$,可得$k>2$,即$2k-p=2k-k^2=-(k-1)^2+1<0$,于是$2k<p$成立。

故$(p-1)!=1\times2\times ...\times k\times...\times 2k\times ...\times (p-1)=2k^2x=2px$,于是$(p-1)! \equiv 0(mod\; p)$。

当$p$不是完全平方数,那么$p$就必然等于两个完全不相等的数$a,b$的乘积,不妨设$a<b$,则有$1<a<b<p$,于是$(p-1)!=1\times ...\times a\times...\times b \times...\times (p-1)=abx=px$,于是也有$(p-1)!\equiv 0(mod \;p)$。

验证必要性:

当$p$为质数,考虑二次剩余式,$x^2\equiv 1(mod\;p)$ ,移项$x^2-1\equiv 0(mod\;p)$,因式分解$(x+1)(x-1)\equiv 0(mod\;p)$,可以得到$x\equiv p-1(mod\;p)$或者$x\equiv 1(mod\;p)$。抛开这两个数,对于$a\in [2,p-2]$,必然存在一个和它不相等的逆元$a^{-1}\in [2,p-2]$满足$aa^{-1}\equiv -1(mod\;p)$。于是必然有$\frac{p-3}{2}$对数相乘的乘积是$1$,即$(p-2)!\equiv 1(mod \;p)$,再乘上$(p-1)$即得到$(p-1)!\equiv p-1(mod\;p)$即$(p-1)!\equiv -1(mod\;p)$。

例题

 

 

 

狄利克雷卷积

1.P2522 - Problem b

知识点

整除分块,狄利克雷卷积

题意

$\sum\limits_{i=a}^{b}\sum\limits_{j=c}^{d}[gcd(i,j)=k]$

题解

$\sum\limits_{i=a}^{b}\sum\limits_{j=c}^{d}[gcd(i,j)=k]=\sum\limits_{ki=a}^{b}\sum\limits_{kj=c}^{d}[gcd(ki,kj)=k]=\sum\limits_{i=\lceil \frac{a}{k}\rceil}^{\lfloor \frac{b}{k}\rfloor}\sum\limits_{j=\lceil \frac{c}{k}\rceil}^{\lfloor \frac{d}{k}\rfloor}[gcd(i,j)=1]$ (这里上限是下取整,下限是上取整。这样不会多枚举。如果反过来的话,就可能多枚举了。)

$=\sum\limits_{i=\lceil \frac{a}{k}\rceil}^{\lfloor \frac{b}{k}\rfloor}\sum\limits_{j=\lceil \frac{c}{k}\rceil}^{\lfloor \frac{d}{k}\rfloor}\sum\limits_{D|gcd(i,j)}\mu(D)=\sum\limits_{D=1}^{min(\lfloor \frac{b}{k}\rfloor,\lfloor \frac{d}{k}\rfloor)}\mu(D)\sum\limits_{i=\lceil \frac{a}{k}\rceil}^{\lfloor \frac{b}{k}\rfloor}\sum\limits_{j=\lceil \frac{c}{k}\rceil}^{\lfloor \frac{d}{k}\rfloor}[D|gcd(i,j)]$

$=\sum\limits_{D=1}^{min(\lfloor \frac{b}{k}\rfloor,\lfloor \frac{d}{k}\rfloor)}\mu(D)\sum\limits_{Di=\lceil \frac{a}{k}\rceil}^{\lfloor \frac{b}{k}\rfloor}\sum\limits_{Dj=\lceil \frac{c}{k}\rceil}^{\lfloor \frac{d}{k}\rfloor}[D|gcd(Di,Dj)]=\sum\limits_{D=1}^{min(\lfloor \frac{b}{k}\rfloor,\lfloor \frac{d}{k}\rfloor)}\mu(D)\sum\limits_{Di=\lceil \frac{a}{k}\rceil}^{\lfloor \frac{b}{k}\rfloor}\sum\limits_{Dj=\lceil \frac{c}{k}\rceil}^{\lfloor \frac{d}{k}\rfloor}1$

$=\sum\limits_{D=1}^{min(\lfloor \frac{b}{k}\rfloor,\lfloor \frac{d}{k}\rfloor)}\mu(D)\sum\limits_{i=\lceil \frac{a}{Dk}\rceil}^{\lfloor \frac{b}{Dk}\rfloor}\sum\limits_{j=\lceil \frac{c}{Dk}\rceil}^{\lfloor \frac{d}{Dk}\rfloor}1=\sum\limits_{D=1}^{min(\lfloor \frac{b}{k}\rfloor,\lfloor \frac{d}{k}\rfloor)}\mu(D)({\lfloor \frac{b}{Dk}\rfloor}-{\lceil \frac{a}{Dk}\rceil}+1)({\lfloor \frac{d}{Dk}\rfloor}-{\lceil \frac{c}{Dk}\rceil}+1)$

整除分块即可,简单证明一下两个整除分块。

$\lfloor \frac{n}{ik} \rfloor$,令$t=\lfloor \frac{n}{lk} \rfloor$ ,$r$为使得$itk\le n$最大的$i$,那么$r=\lfloor \frac{n}{tk} \rfloor=\lfloor\frac{n}{k\lfloor \frac{n}{lk} \rfloor} \rfloor$
$\lceil \frac{n}{ik} \rceil=\lfloor\frac{n+ik-1}{ik} \rfloor$,令$t=\lfloor \frac{n+lk-1}{lk} \rfloor$ ,$r$为使得$itk\le n+ik-1$最大的$i$,那么$r=\lfloor \frac{n-1}{tk-k} \rfloor=\lfloor\frac{n-1}{k\lfloor \frac{n+lk-1}{lk} \rfloor-k} \rfloor$

查看代码
 void Solve(int TIME) {

    int res = 0;
    int t;
    int a, b, c, d, k;cin >> a >> b >> c >> d >> k;
    for (int l = 1, r;l <= min(b / k, d / k);l = r + 1) {
        r = min(b / k, d / k);
        t = (a + l * k - 1) / (l * k); if (k * t - k) r = min(r, (a - 1) / (k * t - k));
        t = b / (l * k);if (k * t) r = min(r, b / (k * t));
        t = (c + l * k - 1) / (l * k); if (k * t - k) r = min(r, (c - 1) / (k * t - k));
        t = d / (l * k);if (k * t) r = min(r, d / (k * t));
        res += (pre_mu[r] - pre_mu[l - 1]) * (1 + b / (l * k) - (a + l * k - 1) / (l * k)) * (1 + d / (l * k) - (c + l * k - 1) / (l * k));
    }
    cout << res << endl;

}

2.P2257 - YY的GCD

知识点

狄利克雷卷积,整除分块

题意

$\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[gcd(x,y)\in Prime]$

题解

$\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}[gcd(x,y)\in Prime]=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}\sum\limits_{d=1}^{min(n,m)}[gcd(i,j)=d][d \in Prime]$

$=\sum\limits_{d=1}^{min(n,m)}\sum\limits_{di=1}^{n}\sum\limits_{dj=1}^{m}[gcd(di,dj)=d][d \in Prime]=\sum\limits_{d=1}^{min(n,m)}\sum\limits_{i=1}^{\lfloor\frac{n}{d} \rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{d} \rfloor}[gcd(i,j)=1][d \in Prime]$

$=\sum\limits_{d=1}^{min(n,m)}\sum\limits_{i=1}^{\lfloor\frac{n}{d} \rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{d} \rfloor}\sum\limits_{D|gcd(i,j)}\mu(D)[d \in Prime]$

$=\sum\limits_{d=1}^{min(n,m)}\sum\limits_{i=1}^{\lfloor\frac{n}{d} \rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{d} \rfloor}\sum\limits_{D=1}^{min(\lfloor\frac{n}{d} \rfloor,\lfloor\frac{m}{d} \rfloor)}\mu(D)[d \in Prime][D|gcd(i,j)]$

$=\sum\limits_{D=1}^{min(\lfloor\frac{n}{d} \rfloor,\lfloor\frac{m}{d} \rfloor)}\mu(D)\sum\limits_{d=1}^{min(n,m)}[d \in Prime]\sum\limits_{Di=1}^{\lfloor\frac{n}{d} \rfloor}\sum\limits_{Dj=1}^{\lfloor\frac{m}{d} \rfloor}[D|gcd(Di,Dj)]$

$=\sum\limits_{d=1}^{min(n,m)}[d \in Prime]\sum\limits_{D=1}^{min(\lfloor\frac{n}{d} \rfloor,\lfloor\frac{m}{d} \rfloor)}\mu(D){\lfloor\frac{n}{dD} \rfloor}{\lfloor\frac{m}{dD} \rfloor}$ (到这一步已经可以整除分块,但是复杂度不够)

令$T=dD$,那么$\sum\limits_{T=1}^{min(n ,m)}{\lfloor\frac{n}{T} \rfloor}{\lfloor\frac{m}{T} \rfloor}\sum\limits_{d|T}[d \in Prime]\mu(\frac{T}{d})$

令$g(d)=[d\in Prime]$,$f(T)=\sum\limits_{d|T}g(d)\mu(\frac{T}{d})$,那么$\sum\limits_{T=1}^{min(n ,m)}{\lfloor\frac{n}{T} \rfloor}{\lfloor\frac{m}{T} \rfloor}\sum\limits_{d|T}g(d)\mu(\frac{T}{d})=\sum\limits_{T=1}^{min(n ,m)}{\lfloor\frac{n}{T} \rfloor}{\lfloor\frac{m}{T} \rfloor}f(T)$

这个$f(T)$显然是可以预处理的,然后便做完了。由于只用对质数计算贡献,复杂度约为$O(NloglogN)$,这是因为质数在自然数的占比约为$logn$,这样就对原本的$NlogN$进一步做了优化。

总时间复杂度$O(NloglogN+T(\sqrt{n}+\sqrt{m}))$ ,这里的$log$均以$e$为底

查看代码
 int f[N];
void init(int mod = MOD) {
    np[0] = np[1] = 1;mu[1] = 1;
    for (int i = 2;i < N;i++) {
        if (!np[i]) {
            p.push_back(i);
            mu[i] = -1;
        }
        for (auto j : p) {
            if (i * j < N) {
                np[i * j] = 1;
                if (i % j == 0) {
                    break;
                }
                else {
                    mu[i * j] = -mu[i];
                }
            }
            else {
                break;
            }
        }
    }
    int t = 0;
    for (int i = 1;i < N;i++) {
        if (np[i]) continue;
        for (int j = 1;i * j < N;j++) {
            f[i * j] += mu[j];t++;
        }
    }
    for (int i = 1;i < N;i++) {
        f[i] += f[i - 1];
    }
}

void Solve(int TIME) {

    int n, m;cin >> n >> m;int res = 0;
    int lim = min(n, m);
    for (int l = 1, r;l <= min(n, m);l = r + 1) {
        r = lim;
        if (n / l) r = min(r, n / (n / l));
        if (m / l) r = min(r, m / (m / l));
        res += (n / l) * (m / l) * (f[r] - f[l - 1]);
    }
    cout << res << endl;

}

 

3.CF1884D - Counting Rhyme

知识点

狄利克雷卷积

题解

注意$a_i\in[1,n]$,这个时候倍数的处理就显得轻松很多。只需要记录$[1,n]$哪些数字出现,然后在循环中枚举这个数字的倍数,打上标记。最坏时间复杂度是$nln(n)$,即$\frac{n}{1}+\frac{n}{2}+...+\frac{n}{n}=n(1+\frac{1}{2}+...+\frac{1}{n})=nln(n)$

 

设一个集合$S$,里面包含$[1,n]$中所有不为$a$数组中任何一个数的倍数的数。

$\sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}\sum\limits_{x\in S}[gcd(a_i,a_j)=x]$

$=\frac{1}{2}\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\sum\limits_{x\in S}[gcd(a_i,a_j)=x]=\frac{1}{2}\sum\limits_{i=1}^{n}cnt_i\sum\limits_{j=1}^{n}cnt_j\sum\limits_{x\in S}[gcd(i,j)=x]$

$=\frac{1}{2}\sum\limits_{x\in S}[gcd(xi,xj)=x]\sum\limits_{xi=1}^{n}cnt_{xi}\sum\limits_{xj=1}^{n}cnt_{xj}=\frac{1}{2}\sum\limits_{x\in S}\sum\limits_{i=1}^{\lfloor\frac{n}{x}\rfloor}cnt_{xi}\sum\limits_{j=1}^{\lfloor\frac{n}{x}\rfloor}cnt_{xj}[gcd(i,j)=1]$

$=\frac{1}{2}\sum\limits_{x\in S}\sum\limits_{i=1}^{\lfloor\frac{n}{x}\rfloor}cnt_{xi}\sum\limits_{j=1}^{\lfloor\frac{n}{x}\rfloor}cnt_{xj}\sum\limits_{d|gcd(i,j)}\mu(d)=\frac{1}{2}\sum\limits_{x\in S}\sum\limits_{i=1}^{\lfloor\frac{n}{x}\rfloor}cnt_{xi}\sum\limits_{j=1}^{\lfloor\frac{n}{x}\rfloor}cnt_{xj}\sum\limits_{d=1}^{\lfloor\frac{n}{x} \rfloor}\mu(d)[d|gcd(i,j)]$

$=\frac{1}{2}\sum\limits_{x\in S}\sum\limits_{di=1}^{\lfloor\frac{n}{x}\rfloor}cnt_{dxi}\sum\limits_{dj=1}^{\lfloor\frac{n}{x}\rfloor}cnt_{dxj}\sum\limits_{d=1}^{\lfloor\frac{n}{x} \rfloor}\mu(d)[d|gcd(di,dj)]=\frac{1}{2}\sum\limits_{x\in S}\sum\limits_{d=1}^{\lfloor\frac{n}{x} \rfloor}\mu(d)\sum\limits_{i=1}^{\lfloor\frac{n}{dx}\rfloor}cnt_{dxi}\sum\limits_{j=1}^{\lfloor\frac{n}{dx}\rfloor}cnt_{dxj}$

令$T=dx$,那么$\frac{1}{2}\sum\limits_{x\in S}\sum\limits_{T=1}^{ n}\mu(\frac{T}{x})\sum\limits_{i=1}^{\lfloor\frac{n}{T}\rfloor}cnt_{Ti}\sum\limits_{j=1}^{\lfloor\frac{n}{T}\rfloor}cnt_{Tj}=\frac{1}{2}\sum\limits_{T=1}^{ n}\sum\limits_{ x|T}\mu(\frac{T}{x})[x\in S](\sum\limits_{i=1}^{\lfloor\frac{n}{T}\rfloor}cnt_{Ti})^2$

令$f(T)=\sum\limits_{ x|T}\mu(\frac{T}{x})[x\in S]$,那么就有$\frac{1}{2}\sum\limits_{T=1}^{n}f(T)(\sum\limits_{i=1}^{\lfloor\frac{n}{T}\rfloor}cnt_{Ti})^2$,$f(T)$可以暴力$nlogn$预处理,而后面的整体暴力求也是$O(nlogn)$复杂度

查看代码
 void Solve(int TIME) {

    int n;cin >> n;
    vi cnt(n + 1);
    vi a(n + 1);for (int i = 1;i <= n;i++) cin >> a[i], cnt[a[i]] += 1;
    vi not_in_s(n + 1);
    for (int i = 1;i <= n;i++) {
        if (cnt[i] == 0) continue;
        for (int j = i;j <= n;j += i) {
            not_in_s[j] = 1;
        }
    }

    vi f(n + 1);

    int res = 0;
    for (int i = 1;i <= n;i++) {
        if (not_in_s[i]) continue;
        for (int j = 1;j * i <= n;j++) {
            f[i * j] += mu[j];
        }
    }

    for (int i = 1;i <= n;i++) {
        int t = 0;
        for (int j = 1;j <= n / i;j++) {
            t += cnt[i * j];
        }
        res += t * t * f[i];
    }

    cout << res / 2 << endl;
}

法2:利用莫比乌斯反演的第二形式

P.S.这种$[gcd(a_i,a_j)=x]$用这个方法会方便很多

$\sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}\sum\limits_{x\in S}[ gcd(a_i,a_j)=x]$

$=\sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}\sum\limits_{x\in S}\sum\limits_{x|D}[D|gcd(a_i,a_j)]\mu(\frac{D}{x})$

$=\sum\limits_{i=1}^{n}[D|a_i]\sum\limits_{j=i+1}^{n}[D|a_j]\sum\limits_{x\in S}\sum\limits_{x|D}\mu(\frac{D}{x})$

$=\sum\limits_{x\in S}\sum\limits_{x|D}\sum\limits_{i=1}^{n}[D|a_i]\sum\limits_{j=i+1}^{n}[D|a_j]\mu(\frac{D}{x})$

 

$F(D)=\sum\limits_{i=1}^{n}[D|a_i]\sum\limits_{j=i+1}^{n}[D|a_j]=\frac{1}{2}\left((\sum\limits_{i=1}^{n}[D|a_i])^2-\sum\limits_{i=1}^n[D|a_i]\right)$

原式$=\sum\limits_{x\in S}\sum\limits_{x|D}F(D)\mu(\frac{D}{x})$

查看代码
 
    int n;cin >> n;
    vi cnt(n + 1);
    vi a(n + 1);for (int i = 1;i <= n;i++) cin >> a[i], cnt[a[i]] += 1;
    vi not_in_s(n + 1);
    for (int i = 1;i <= n;i++) {
        if (cnt[i] == 0) continue;
        for (int j = i;j <= n;j += i) {
            not_in_s[j] = 1;
        }
    }

    vi f(n + 1);

    for (int i = 1;i <= n;i++) {
        for (int j = 1;j * i <= n;j++) {
            f[i] += cnt[i * j];
        }
    }
    for (int i = 1;i <= n;i++) f[i] = (f[i] * f[i] - f[i]) / 2;
    int res = 0;
    for (int i = 1;i <= n;i++) {
        if (not_in_s[i]) continue;
        for (int j = 1;j * i <= n;j++) {
            res += f[i * j] * mu[j];
        }
    }

    cout << res << endl;

}

 

4.P3312 - 数表

知识点

狄利克雷卷积,整除分块

题解

设约数和函数$\sigma$

$\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}\sigma(gcd(i,j))[\sigma(gcd(i,j))\le a]=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}\sum\limits_{d=1}^{min(n,m)}\sigma(d)[gcd(i,j)=d][\sigma(d)\le a]$

$=\sum\limits_{di=1}^{n}\sum\limits_{dj=1}^{m}\sum\limits_{d=1}^{min(n,m)}\sigma(d)[gcd(di,dj)=d][\sigma(d)\le a]=\sum\limits_{i=1}^{\lfloor\frac{n}{d} \rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{d} \rfloor}\sum\limits_{d=1}^{min(n,m)}\sigma(d)[gcd(i,j)=1][\sigma(d)\le a]$

$=\sum\limits_{i=1}^{\lfloor\frac{n}{d} \rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{d} \rfloor}\sum\limits_{d=1}^{min(n,m)}\sigma(d)\sum\limits_{D|gcd(i,j)}\mu(D)[\sigma(d)\le a]$

$=\sum\limits_{i=1}^{\lfloor\frac{n}{d} \rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{d} \rfloor}\sum\limits_{d=1}^{min(n,m)}\sigma(d)\sum\limits_{D=1}^{min(\lfloor\frac{n}{d} \rfloor,\lfloor\frac{m}{d} \rfloor)}\mu(D)[D|gcd(i,j)][\sigma(d)\le a]$

$=\sum\limits_{Di=1}^{\lfloor\frac{n}{d} \rfloor}\sum\limits_{Dj=1}^{\lfloor\frac{m}{d} \rfloor}\sum\limits_{d=1}^{min(n,m)}\sigma(d)\sum\limits_{D=1}^{min(\lfloor\frac{n}{d} \rfloor,\lfloor\frac{m}{d} \rfloor)}\mu(D)[D|gcd(Di,Dj)][\sigma(d)\le a]$

$=\sum\limits_{i=1}^{\lfloor\frac{n}{Dd} \rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{Dd} \rfloor}\sum\limits_{d=1}^{min(n,m)}\sigma(d)\sum\limits_{D=1}^{min(\lfloor\frac{n}{d} \rfloor,\lfloor\frac{m}{d} \rfloor)}\mu(D)[\sigma(d)\le a]$

令$T=Dd$,则有 $\sum\limits_{i=1}^{\lfloor\frac{n}{T} \rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{T} \rfloor}\sum\limits_{d=1}^{min(n,m)}\sigma(d)\sum\limits_{T=1}^{min(n,m)}\mu(\frac{T}{d})[\sigma(d)\le a]=\sum\limits_{T=1}^{min(n,m)}\mu(\frac{T}{d})\sum\limits_{i=1}^{\lfloor\frac{n}{T} \rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{T} \rfloor}\sum\limits_{d=1}^{min(n,m)}\sigma(d)[\sigma(d)\le a]$

$=\sum\limits_{T=1}^{min(n,m)}{\lfloor\frac{n}{T} \rfloor}{\lfloor\frac{m}{T} \rfloor}\sum\limits_{d=1}^{min(n,m)}\sigma(d)\mu(\frac{T}{d})[\sigma(d)\le a]$

令$g(T)=\sum\limits_{d=1}^{min(n,m)}\sigma(d)\mu(\frac{T}{d})[\sigma(d)\le a]$

那么有$\sum\limits_{T=1}^{min(n,m)}{\lfloor\frac{n}{T} \rfloor}{\lfloor\frac{m}{T} \rfloor}g(T)$ ,只有$\sigma(d)\le a$会对其产生贡献

多组询问,考虑将询问的$a$从小到大排序处理。再将$\sigma$按大小排序,进一步优化。

然后使用树状数组每次加入新的贡献,再计算这个询问的答案。

树状数组最坏情况需要加入$nlogn$次,每次复杂度是$logn$ ,复杂度$O(nlog^2n)$。

总时间复杂度$O(nlog^2n+q(\sqrt{n}logn))$

查看代码
 void init(int mod = MOD) {


    np[0] = np[1] = 1;mu[1] = 1;
    sigma[1] = 1;low[1] = 1;
    for (int i = 2;i < N;i++) {
        if (!np[i]) {
            p.push_back(i);
            mu[i] = -1;
            sigma[i] = i + 1;low[i] = i;
        }
        for (auto j : p) {
            if (i * j < N) {
                np[i * j] = 1;
                if (i % j == 0) {
                    low[i * j] = low[i] * j;
                    if (i == low[i]) sigma[i * j] = sigma[i] * j + 1;
                    else sigma[i * j] = sigma[i / low[i]] * sigma[low[i] * j];
                    break;
                }
                else {
                    low[i * j] = j;
                    mu[i * j] = -mu[i];
                    sigma[i * j] = sigma[i] * (j + 1);
                }
            }
            else {
                break;
            }
        }
    }
}


struct Q {
    int a, id;
    int n, m;
    bool friend operator<(Q a, Q b) {
        return a.a < b.a;
    }
};

void Solve(int TIME) {

    int q;cin >> q;
    vc<Q> qu(q + 1);
    for (int i = 1;i <= q;i++) {
        int n, m, a;cin >> n >> m >> a;
        qu[i] = { a,i,n,m };
    }
    BIT tr(1e5 + 10);
    vi ans(q + 1);
    vc<pi> Sigma(1e5 + 1);
    for (int i = 1;i <= 1e5;i++) {
        Sigma[i] = { sigma[i],i };
    }
    sort(Sigma.begin() + 1, Sigma.end());
    sort(qu.begin() + 1, qu.end());
    int now = 0;
    for (int i = 1;i <= q;i++) {
        auto [a, id, n, m] = qu[i];
        int lim = min(n, m);
        for (int j = now + 1;j <= 1e5;j++) {
            if (Sigma[j].first > a) {
                now = j - 1;
                break;
            }
            for (int k = 1;k * Sigma[j].second <= 1e5;k++) {
                tr.add(k * Sigma[j].second, Sigma[j].first * mu[k] % MOD);
            }
        }
        for (int l = 1, r;l <= lim;l = r + 1) {
            r = lim;
            if (n / l) r = min(r, n / (n / l));
            if (m / l) r = min(r, m / (m / l));
            ans[id] = (ans[id] + tr.query(l, r) * (n / l) % MOD * (m / l) % MOD) % MOD;
        }
    }
    for (int i = 1;i <= q;i++)   cout << (ans[i] % MOD + MOD) % MOD << endl;

}

5.P2398 - GCD SUM

知识点

莫比乌斯反演,整除分块

题解

$\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}gcd(i,j)=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{m}\sum\limits_{k=1}^{min(n,m)}k[gcd(i,j)=k]$

$=\sum\limits_{i=1}^{\lfloor\frac{n}{k}\rfloor}\sum\limits_{j=1}^{\lfloor \frac{m}{k}\rfloor}\sum\limits_{k=1}^{min(n,m)}k\sum\limits_{d|gcd(i,j)}\mu(d)=\sum\limits_{i=1}^{\lfloor\frac{n}{k}\rfloor}\sum\limits_{j=1}^{\lfloor \frac{m}{k}\rfloor}\sum\limits_{k=1}^{min(n,m)}k\sum\limits_{d=1}^{min(\lfloor \frac{n}{k}\rfloor,\lfloor \frac{m}{k}\rfloor)}\mu(d)[d|gcd(i,j)]$

$=\sum\limits_{i=1}^{\lfloor\frac{n}{dk}\rfloor}\sum\limits_{j=1}^{\lfloor \frac{m}{dk}\rfloor}\sum\limits_{k=1}^{min(n,m)}k\sum\limits_{d=1}^{min(\lfloor \frac{n}{k}\rfloor,\lfloor \frac{m}{k}\rfloor)}\mu(d)$

$=\sum\limits_{i=1}^{\lfloor\frac{n}{dk}\rfloor}\sum\limits_{j=1}^{\lfloor \frac{m}{dk}\rfloor}\sum\limits_{k=1}^{min(n,m)}k\sum\limits_{d=1}^{min(\lfloor \frac{n}{k}\rfloor,\lfloor \frac{m}{k}\rfloor)}\mu(d)=\sum\limits_{k=1}^{min(n,m)}k\sum\limits_{d=1}^{min(\lfloor \frac{n}{k}\rfloor,\lfloor \frac{m}{k}\rfloor)}\mu(d)\lfloor\frac{n}{dk}\rfloor\lfloor\frac{m}{dk}\rfloor$

$=\sum\limits_{T=1}^{min(n,m)}\mu(\frac{T}{k})\sum\limits_{k|T}k\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor$

$=\sum\limits_{T=1}^{min(n,m)}\varphi(T)\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor$ ,(由$\mu*id=\varphi$得出)

推到这里才发现$n=m$,那么直接就是$\sum\limits_{T=1}^{n}\varphi(T)(\lfloor\frac{n}{T}\rfloor)^2$

查看代码
 void Solve(int TIME) {

    int n;cin >> n;
    int res = 0;
    for (int l = 1, r;l <= n;l = r + 1) {
        r = n;
        if (n / l) r = min(r, n / (n / l));
        res += (pre_phi[r] - pre_phi[l - 1]) * (n / l) * (n / l);
    }
    cout << res << endl;

}

 

6.AGC038C - LCMs

知识点

狄利克雷卷积

题意

$\sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}lcm(A_i,A_j)$

题解

$\sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}lcm(A_i,A_j)=\frac12(\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}lcm(A_i,A_j)-\sum\limits_{i=1}^{n}A_i)$

$=\frac12(\sum\limits_{i=1}^{N}\sum\limits_{j=1}^{N}cnt_i \cdot  cnt_j\cdot lcm(i,j)-\sum\limits_{i=1}^{N}i\cdot cnt_i)$

考虑计算$res1=\sum\limits_{i=1}^{N}\sum\limits_{j=1}^{N}cnt_i \cdot  cnt_j\cdot lcm(i,j)=\sum\limits_{i=1}^{N}\sum\limits_{j=1}^{N}cnt_i \cdot  cnt_j\cdot \frac{ij}{gcd(i,j)}$

$=\sum\limits_{i=1}^{N}\sum\limits_{j=1}^{N}\sum\limits_{k=1}^{N}cnt_i \cdot  cnt_j\cdot \frac{ij}{k}[gcd(i,j)=k]=\sum\limits_{i=1}^{\lfloor \frac{N}{k} \rfloor}\sum\limits_{j=1}^{\lfloor \frac{N}{k} \rfloor}\sum\limits_{k=1}^{N}cnt_{ki} \cdot  cnt_{kj}\cdot \frac{ki\cdot kj}{k}[gcd(i,j)=1]$

 $=\sum\limits_{i=1}^{\lfloor \frac{N}{k} \rfloor}\sum\limits_{j=1}^{\lfloor \frac{N}{k} \rfloor}\sum\limits_{k=1}^{N}cnt_{ki} \cdot  cnt_{kj}\cdot kij\sum\limits_{d|gcd(i,j)}\mu(d)=\sum\limits_{i=1}^{\lfloor \frac{N}{k} \rfloor}\sum\limits_{j=1}^{\lfloor \frac{N}{k} \rfloor}\sum\limits_{k=1}^{N}cnt_{ki} \cdot  cnt_{kj}\cdot kij\sum\limits_{d=1}^{\lfloor \frac{N}{k} \rfloor}\mu(d)[d|gcd(i,j)]$

$=\sum\limits_{i=1}^{\lfloor \frac{N}{dk} \rfloor}\sum\limits_{j=1}^{\lfloor \frac{N}{dk} \rfloor}\sum\limits_{k=1}^{N}cnt_{dki} \cdot  cnt_{dkj}\cdot d^2kij\sum\limits_{d=1}^{\lfloor \frac{N}{k} \rfloor}\mu(d)$

令$T=dk$,则有$\sum\limits_{i=1}^{\lfloor \frac{N}{T} \rfloor}\sum\limits_{j=1}^{\lfloor \frac{N}{T} \rfloor}\sum\limits_{k|T}cnt_{Ti} \cdot  cnt_{Tj}\cdot T\frac{T}{k}ij\sum\limits_{T=1}^{N}\mu(\frac{T}{k})$

$=\sum\limits_{T=1}^{N}T\sum\limits_{k|T}\mu(\frac{T}{k})\frac{T}{k}\sum\limits_{i=1}^{\lfloor \frac{N}{T} \rfloor}\sum\limits_{j=1}^{\lfloor \frac{N}{T} \rfloor}cnt_{Ti} \cdot  cnt_{Tj}\cdot ij=\sum\limits_{T=1}^{N}T\sum\limits_{k|T}\mu(\frac{T}{k})\frac{T}{k}(\sum\limits_{i=1}^{\lfloor \frac{N}{T} \rfloor}cnt_{Ti} \cdot i)^2$

$=\sum\limits_{T=1}^{N}T\sum\limits_{k|T}\mu(k)k(\sum\limits_{i=1}^{\lfloor \frac{N}{T} \rfloor}cnt_{Ti} \cdot i)^2$

最后的答案$\frac12(\sum\limits_{T=1}^{N}T\sum\limits_{k|T}\mu(k)k(\sum\limits_{i=1}^{\lfloor \frac{N}{T} \rfloor}cnt_{Ti} \cdot i)^2-\sum\limits_{i=1}^{N}i\cdot cnt_i)$

查看代码
 void Solve(int TIME) {

    int n;cin >> n;
    for (int i = 1;i <= n;i++) {
        int x;cin >> x;cnt[x] += 1;
    }
    for (int i = 1;i <= 1e6;i++) {
        for (int j = 1;j * i <= 1e6;j++) {
            f[i * j] = (f[i * j] + mu[i] * i % MOD) % MOD;
        }
    }

    int res = 0;
    for (int T = 1;T <= 1e6;T++) {
        int t = 0;
        for (int j = 1;j <= 1e6 / T;j++) {
            t = (t + cnt[T * j] * j % MOD) % MOD;
        }
        res = (res + T * f[T] % MOD * t % MOD * t % MOD) % MOD;
    }
    for (int i = 1;i <= 1e6;i++) {
        res = (res - 1ll * i * cnt[i] % MOD) % MOD;
    }
    res = res * inv(2) % MOD;
    cout << (res % MOD + MOD) % MOD << endl;

}

 

或者改成下一题用莫反第二形式,代码如下

查看代码
 void Solve(int TIME) {

    int n;cin >> n;
    const int M = 1e6;
    vi cnt(M + 1);
    vi a(n + 1);for (int i = 1;i <= n;i++) cin >> a[i], cnt[a[i]] += 1;
    vi f(M + 1);
    for (int i = 1;i <= M;i++) {
        for (int j = i;j <= M;j += i) {
            f[i] = (f[i] + cnt[j] * j % MOD) % MOD;
        }
    }

    for (int i = 1;i <= M;i++) f[i] = f[i] * f[i] % MOD;

    int res = 0;
    for (int i = 1;i <= M;i++) {
        int t = 0;
        for (int j = 1;j * i <= M;j++) {
            t = (t + f[i * j] * mu[j] % MOD) % MOD;
        }
        res = (res + t * inv(i) % MOD) % MOD;
    }
    for (int i = 1;i <= n;i++) res = (res - a[i] + MOD) % MOD;
    cout << res * inv(2) % MOD << endl;

}

 

7.P3911 最小公倍数之和

知识点

莫比乌斯容斥

题意

$\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}lcm(a_i,a_j)$

题解

两种方法,一种是将式子的$a_i$转化为枚举值域然后记录数组$cnt_i$。

另一种方法是使用莫比乌斯反演的第二种形式。

上一题跟这个差不多,用的是法一。这里用第二种方法写一下。

$\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}lcm(a_i,a_j)=\sum\limits_{i=1}^{n}a_i\sum\limits_{j=1}^{n}a_j \frac{1}{gcd(a_i,a_j)}$

$=\sum\limits_{i=1}^{n}a_i\sum\limits_{j=1}^{n}a_j\sum\limits_{d=1}^{n} \frac{1}{d}[gcd(a_i,a_j)=d]$

$=\sum\limits_{i=1}^{n}a_i\sum\limits_{j=1}^{n}a_j\sum\limits_{d=1}^{n} \frac{1}{d}\sum\limits_{D=d,2d,...}[D|gcd(a_i,a_j)]\mu(\frac{D}{d})$

$=\sum\limits_{D=d,2d,...}\sum\limits_{i=1}^{n}a_i[D|a_i]\sum\limits_{j=1}^{n}a_j[D|a_j]\sum\limits_{d=1}^{n} \frac{1}{d}\mu(\frac{D}{d})$

$=\sum\limits_{D=d,2d,...}(\sum\limits_{i=1}^{n}a_i[D|a_i])^2\sum\limits_{d=1}^{n} \frac{1}{d}\mu(\frac{D}{d})$

令$F(D)=(\sum\limits_{i=1}^{n}a_i[D|a_i])^2$

原式$=\sum\limits_{d=1}^{n}\frac{1}{d} \sum\limits_{D=d,2d,...}F(D)\mu(\frac{D}{d})$

其中$F(x)$可以$O(nlogn)$预处理,而后面这个也可以$O(nlogn)$解决。

查看代码
 void Solve(int TIME) {

    int n;cin >> n;
    const int M = 5e4;
    vi cnt(M + 1);
    vi a(n + 1);for (int i = 1;i <= n;i++) cin >> a[i], cnt[a[i]] += 1;
    vi f(M + 1);
    for (int i = 1;i <= M;i++) {
        for (int j = i;j <= M;j += i) {
            f[i] += cnt[j] * j;
        }
    }

    for (int i = 1;i <= M;i++) f[i] = f[i] * f[i];

    int res = 0;
    for (int i = 1;i <= M;i++) {
        int t = 0;
        for (int j = 1;j * i <= M;j++) {
            t += f[i * j] * mu[j];
        }
        res += t / i;
    }
    cout << res << endl;

}

 

8.HDU 7325 - GCD Magic

知识点

狄利克雷卷积,杜教筛

题意

​$\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[\gcd(2^i-1,2^j-1)]^k$

题解

推公式或打表可以得出$gcd(2^{i}-1,2^{j}-1)=gcd(2^i-1,2^j-2^i)=gcd(2^i-1,2^i(2^{j-i}-1))$

$=gcd(2^i-1,2^{j-i}-1)=...=2^{gcd(i,j)}-1$

法1.

$\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[\gcd(2^i-1,2^j-1)]^k=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}(2^{gcd(i,j)}-1)^k$

$=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\sum\limits_{d=1}^{n}(2^{d}-1)^k[gcd(i,j)=d]=\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{d=1}^{n}(2^{d}-1)^k[gcd(i,j)=1]$

$=\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{d=1}^{n}(2^{d}-1)^k\sum\limits_{D|gcd(i,j)}\mu(D)=\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{d=1}^{n}(2^{d}-1)^k\sum\limits_{D=1}^{\lfloor\frac{n}{d} \rfloor}\mu(D)[D|gcd(i,j)]$

$=\sum\limits_{i=1}^{\lfloor\frac{n}{dD}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{n}{dD}\rfloor}\sum\limits_{d=1}^{n}(2^{d}-1)^k\sum\limits_{D=1}^{\lfloor\frac{n}{d} \rfloor}\mu(D)[D|gcd(Di,Dj)]$

令$T=dD$,那么有$\sum\limits_{i=1}^{\lfloor\frac{n}{T}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{n}{T}\rfloor}\sum\limits_{T=1}^{n}\sum\limits_{d|T}(2^{d}-1)^k\mu(\frac{T}{d})=\sum\limits_{T=1}^{n}(\lfloor\frac{n}{T}\rfloor)^2\sum\limits_{d|T}(2^{d}-1)^k\mu(\frac{T}{d})$

令$f(d)=(2^d-1)^k$,$g(T)=\sum\limits_{d|T}f(d)\mu(\frac{T}{d})$, 那么有$\sum\limits_{T=1}^{n}(\lfloor\frac{n}{T}\rfloor)^2g(T)$

现在即求$g=f*\mu$的前缀和$G$,考虑将$g$卷上$1$,则$\sum\limits_{i=1}^{n}f(i)=\sum\limits_{i=1}^{n}(g*1)(i)=\sum\limits_{i=1}^{n}\sum\limits_{j|i}g(j)=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{\lfloor \frac{n}{i} \rfloor}g(j)=\sum\limits_{i=1}^{n}G(\lfloor \frac{n}{i} \rfloor)$

即有$G(n)=\sum\limits_{i=1}^{n}f(i)-\sum\limits_{i=2}^{n}G(\lfloor \frac{n}{i} \rfloor)$,这一步也就是杜教筛的结论。

然后计算$f$的前缀和,$F(n)=\sum\limits_{i=1}^{n}(2^i-1)^k=\sum\limits_{i=1}^{n}\sum\limits_{j=0}^{k}C_{k}^{j}2^{ij}(-1)^{k-j}=\sum\limits_{j=0}^{k}C_{k}^{j}(-1)^{k-j}\sum\limits_{i=1}^{n}(2^{j})^i$

后面是个等比数列,然后可以每次$klogn$查询$f$的前缀和

预处理出$1e6$范围的 $G$,然后杜教筛即可。复杂度$O(T(n^{\frac23}logn^\frac32+k\sqrt{n}logn))$

查看代码
int g[N];

int sum_f(int n, int k) {
    int res = 0;
    for (int j = 0;j <= k;j++) {
        int sgn = (k - j) % 2 == 1 ? MOD - 1 : 1;
        int q = (1 << j);
        if (j == 0) {
            res = (res + n * sgn % MOD) % MOD;
        }
        else {
            res = add(res, sgn * comb(k, j) % MOD * (q * (qp(q, n) - 1) % MOD * inv(q - 1) % MOD) % MOD);
        }
    }
    return norm(res);
}

int Du_sum_g(int n, int pre_f[], umap<int, int>& mp, int k) {
    if (n < N) return pre_f[n];
    if (mp.count(n)) return mp[n];
    int res = sum_f(n, k);
    for (int l = 2, r;l <= n;l = r + 1) {
        r = n;
        if (n / l) r = min(r, n / (n / l));
        res = (res - Du_sum_g(n / l, pre_f, mp, k) * (r - l + 1) % MOD) % MOD;
    }
    return mp[n] = res;
}

void Solve(int TIME) {

    int n, k;cin >> n >> k;
    umap<int, int> mp;
    for (int i = 0;i <= 1e6;i++) g[i] = 0;
    for (int i = 1;i <= 1e6;i++) {
        int t = qp((qp(2, i) - 1), k);
        for (int j = 1;j * i <= 1e6;j++) {
            g[i * j] = (g[i * j] + mu[j] * t % MOD) % MOD;
        }
    }
    for (int i = 1;i <= 1e6;i++) {
        g[i] = (g[i - 1] + g[i]) % MOD;
    }

    int res = 0;
    for (int l = 1, r;l <= n;l = r + 1) {
        r = n;
        if (n / l) r = min(r, n / (n / l));
        res = add(res, (n / l) * (n / l) % MOD * (Du_sum_g(r, g, mp, k) - Du_sum_g(l - 1, g, mp, k)) % MOD);
    }
    cout << norm(res) << endl;

}

法2.

$\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[\gcd(2^i-1,2^j-1)]^k=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}(2^{gcd(i,j)}-1)^k$

$=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\sum\limits_{d=1}^{n}(2^{d}-1)^k[gcd(i,j)=d]=\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{d=1}^{n}(2^{d}-1)^k[gcd(i,j)=1]$

$=\sum\limits_{d=1}^{n}(2^{d}-1)^k(\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}2\varphi(i)-1)$

考虑对后面的$\lfloor\frac{n}{d}\rfloor$整除分块,杜教筛处理后面的$\varphi$的前缀和,前面的也可以通过上面所说的变成等比数列,每次$klogn$复杂度的查询。

根据$\varphi*1=id$做杜教筛

$\sum\limits_{i=1}^{n}id(i)=\sum\limits_{i=1}^{n}\varphi(i)\sum\limits_{j=1}^{\lfloor \frac{n}{i} \rfloor}1=\sum\limits_{i=1}^{n}1\sum\limits_{j=1}^{\lfloor \frac{n}{i} \rfloor}\varphi(j)=\sum\limits_{i=1}^{n}\varphi(i)+\sum\limits_{i=2}^{n}\sum\limits_{j=1}^{\lfloor \frac{n}{i} \rfloor}\varphi(j)$

即$\sum\limits_{i=1}^{n}\varphi(i)=\sum\limits_{i=1}^{n}id(i)-\sum\limits_{i=2}^{n}\sum\limits_{j=1}^{\lfloor \frac{n}{i} \rfloor}\varphi(j)=\frac{n(n+1)}{2}-\sum\limits_{i=2}^{n}\sum\limits_{j=1}^{\lfloor \frac{n}{i} \rfloor}\varphi(j)$

查看代码
 int g[N];

int sum_f(int n, int k) {
    int res = 0;
    for (int j = 0;j <= k;j++) {
        int sgn = (k - j) % 2 == 1 ? MOD - 1 : 1;
        int q = (1 << j);
        if (j == 0) {
            res = (res + n * sgn % MOD) % MOD;
        }
        else {
            res = add(res, sgn * comb(k, j) % MOD * (q * (qp(q, n) - 1) % MOD * inv(q - 1) % MOD) % MOD);
        }
    }
    return norm(res);
}


int Du_sum_phi(int n, int pre_f[], umap<int, int>& mp) {
    if (n < N) return pre_f[n];
    if (mp.count(n)) return mp[n];
    int res = n * (n + 1) / 2;
    for (int l = 2, r;l <= n;l = r + 1) {
        r = n;
        if (n / l) r = min(r, n / (n / l));
        res = (res - Du_sum_phi(n / l, pre_f, mp) * (r - l + 1) % MOD) % MOD;
    }
    return mp[n] = res;
}


umap<int, int> mp_sum_phi;
void Solve(int TIME) {

    int n, k;cin >> n >> k;
    int res = 0;
    for (int l = 1, r;l <= n;l = r + 1) {
        r = n;
        if (n / l) r = min(r, n / (n / l));
        res = add(res, (sum_f(r, k) - sum_f(l - 1, k)) % MOD * (2 * Du_sum_phi(n / l, pre_phi, mp_sum_phi) % MOD - 1) % MOD);
    }
    cout << norm(res) << endl;

}

 

9.[2021CCPC威海热身赛] Number Theory

知识点

打表

题意

$\sum\limits_{k=1}^{n} \sum\limits_{i|k} \sum\limits_{j|i}\lambda(i)\lambda(j)$,其中$\lambda(x)=(-1)^{\sum\limits_{i=1}^{k}c_i}$,$x=\prod\limits_{i=1}^{k}p_i^{c_i}$ 。$1\le n\le 10^{12}$

题解

打表易知$\sum\limits_{d|n}\lambda(d)=[n=a^2,a\in N^+]$

$\sum\limits_{k=1}^{n} \sum\limits_{i|k} \sum\limits_{j|i}\lambda(i)\lambda(j)=\sum\limits_{k=1}^{n} \sum\limits_{i|k}\lambda(i) [i=a^2,a\in N^{+}]$

设$f(i)=\lambda(i) [i=a^2,a\in N^{+}]=[i=a^2,a\in N^{+}]$,那么就有$\sum\limits_{k=1}^{n} \sum\limits_{i|k}f(i)=\sum\limits_{i=1}^{n}f(i)\lfloor \frac{n}{i} \rfloor$

这样枚举平方数即可,复杂度$O(\sqrt{n})$

查看代码
 void Solve(int TIME) {

    int n;cin >> n;int res = 0;
    for (int i = 1;i * i <= n;i++) {
        res = (res + n / (i * i) % MOD) % MOD;
    }
    cout << res << endl;

}

 

10.2019ICPC西安区域赛 D - Easy Problem

知识点

欧拉降幂,莫比乌斯反演

题解

$\sum\limits_{a_1=1}^{m}\sum\limits_{a_2=1}^{m}...\sum\limits_{a_n=1}^{m}(a_1a_2...a_n)^k[gcd(a_1,a_2,...,a_n)=d]$

$=d^{nk}\sum\limits_{a_1=1}^{\lfloor \frac{m}{d }\rfloor}\sum\limits_{a_2=1}^{\lfloor \frac{m}{d }\rfloor}...\sum\limits_{a_n=1}^{\lfloor \frac{m}{d }\rfloor}(a_1a_2...a_n)^k[gcd(da_1,da_2,...,da_n)=d]$

$=d^{nk}\sum\limits_{a_1=1}^{\lfloor \frac{m}{d }\rfloor}\sum\limits_{a_2=1}^{\lfloor \frac{m}{d }\rfloor}...\sum\limits_{a_n=1}^{\lfloor \frac{m}{d }\rfloor}(a_1a_2...a_n)^k[gcd(a_1,a_2,...,a_n)=1]$

$=d^{nk}\sum\limits_{a_1=1}^{\lfloor \frac{m}{d }\rfloor}\sum\limits_{a_2=1}^{\lfloor \frac{m}{d }\rfloor}...\sum\limits_{a_n=1}^{\lfloor \frac{m}{d }\rfloor}(a_1a_2...a_n)^k\sum\limits_{D|gcd(a_1,a_2,...,a_n)}\mu(D)$

$=d^{nk}\sum\limits_{a_1=1}^{\lfloor \frac{m}{d }\rfloor}\sum\limits_{a_2=1}^{\lfloor \frac{m}{d }\rfloor}...\sum\limits_{a_n=1}^{\lfloor \frac{m}{d }\rfloor}(a_1a_2...a_n)^k\sum\limits_{D=1}^{\lfloor \frac{m}{d }\rfloor}\mu(D)[D|gcd(a_1,a_2,...,a_n)]$

$=d^{nk}D^{nk}\sum\limits_{a_1=1}^{\lfloor \frac{m}{dD }\rfloor}\sum\limits_{a_2=1}^{\lfloor \frac{m}{dD }\rfloor}...\sum\limits_{a_n=1}^{\lfloor \frac{m}{dD }\rfloor}(a_1a_2...a_n)^k\sum\limits_{D=1}^{\lfloor \frac{m}{d }\rfloor}\mu(D)[D|gcd(Da_1,Da_2,...,Da_n)]$

$=(dD)^{nk}\sum\limits_{a_1=1}^{\lfloor \frac{m}{dD }\rfloor}\sum\limits_{a_2=1}^{\lfloor \frac{m}{d D}\rfloor}...\sum\limits_{a_n=1}^{\lfloor \frac{m}{dD }\rfloor}(a_1a_2...a_n)^k\sum\limits_{D=1}^{\lfloor \frac{m}{d }\rfloor}\mu(D)$

$=\sum\limits_{D=1}^{\lfloor \frac{m}{d }\rfloor}\mu(D)(dD)^{nk}\sum\limits_{a_1=1}^{\lfloor \frac{m}{d D}\rfloor}a_1^k\sum\limits_{a_2=1}^{\lfloor \frac{m}{d D}\rfloor}a_2^k...\sum\limits_{a_n=1}^{\lfloor \frac{m}{d D}\rfloor}a_n^k$

$=d^{nk}\sum\limits_{D=1}^{\lfloor \frac{m}{d }\rfloor}\mu(D)D^{nk}(\sum\limits_{i=1}^{\lfloor \frac{m}{d D}\rfloor}i^k)^n$

然后用欧拉降幂即可。

可以线性筛预处理$i^k$,与$i^k$的前缀和,然后$O(mlog(mod))$扫过去。

查看代码
 int k;

void init(int mod = MOD) {
    
    np[0] = np[1] = 1;mu[1] = 1;idk[1] = 1;
    for (int i = 2;i < N;i++) {
        if (!np[i]) {
            p.push_back(i);
            mu[i] = -1;
            idk[i] = qp(i, k);
        }
        for (auto j : p) {
            if (i * j < N) {
                np[i * j] = 1;
                if (i % j == 0) {
                    idk[i * j] = idk[i] * idk[j] % MOD;
                    break;
                }
                else {
                    mu[i * j] = -mu[i];
                    idk[i * j] = idk[i] * idk[j] % MOD;
                }
            }
            else {
                break;
            }
        }
    }
    for (int i = 1;i < N;i++) {
        sum_idk[i] = (idk[i] + sum_idk[i - 1]) % MOD;
    }
}

int phi_mod = getphi(MOD);

void Solve(int TIME) {

    string n;cin >> n;
    int m, d;cin >> m >> d >> k;
    init();
    int res = qp(d, k);
    int nn = exp_euler(n, MOD);
    res = qp(res, nn);
    int t = 0;
    for (int i = 1;i <= m / d;i++) {
        t = (t + mu[i] * qp(idk[i], nn) % MOD * qp(sum_idk[m / d / i], nn) % MOD) % MOD;
    }
    cout << norm(res * t) << endl;

}

 

11.CF1900D - Small GCD

知识点

欧拉反演,gcd的调和级数容斥,莫比乌斯容斥

题意

$\sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}gcd(a_i,a_j) \cdot(n-j)$

题解

考虑右端点递增,然后思考如何去维护左侧所有区间的信息。

$f(t)$表示$gcd(a_i,a_j)=t$的对数

考虑容斥:$g(t)$表示$t|gcd(a_i,a_j)$的对数

$g(t)=f(t)+f(2t)+f(3t)+...$

那么$f(t)=g(t)-f(2t)-f(3t)-...$

而$g(t)$可以通过预处理得到,对每个数的因子进行判断记录贡献即可。

答案是$\sum\limits_{i=1}^{N}i\cdot f(i)$

查看代码
 vvc<int> factor(N + 10);
 
 
void Solve(int TIME) {
 
    int T;cin >> T;
    for (int i = 1;i <= N;i++) {
        for (int j = 1;j * i <= N;j++) {
            factor[i * j].push_back(i);
        }
    }
    while (T--) {
        int n;cin >> n;
        vi a(n + 1);for (int i = 1;i <= n;i++) cin >> a[i];
        sort(a.begin() + 1, a.end());
        vi cnt(N + 10);
        vi f(N + 10);
        for (int i = 1;i <= n;i++) {
            for (auto x : factor[a[i]]) {
                f[x] += cnt[x] * (n - i);
                cnt[x] += 1;
            }
        }
        vi F(N + 10);
        int res = 0;
        for (int i = N;i >= 1;i--) {
            F[i] = f[i];
            for (int j = i + i;j <= N;j += i) {
                F[i] -= F[j];
            }
            res += F[i] * i;
        }
 
        cout << res << endl;
    }
 
}

法2.

利用欧拉反演:$gcd(a_i,a_j)=\sum\limits_{d|gcd(a_i,a_j)}\varphi(d)$

$\sum\limits_{i=1}^{j-1}\sum\limits_{j=1}^{n}gcd(a_i,a_j) \cdot (n-j)=\sum\limits_{i=1}^{j-1}[d|a_i]\sum\limits_{j=1}^{n}[d|a_j](n-j)\sum\limits_{d=1}^{n}\varphi(d) $    

$=\sum\limits_{d=1}^{N}\sum\limits_{j=1}^{n}[d|a_j](n-j)\sum\limits_{i=1}^{j-1}[d|a_i]$

枚举右端点,枚举当前$a_j$的约数记录贡献即可。

查看代码
 void Solve(int TIME) {

    int T;cin >> T;
    vvc<int> factor(N + 10);
    for (int i = 1;i <= N;i++) {
        for (int j = 1;j * i <= N;j++) {
            factor[i * j].push_back(i);
        }
    }

    while (T--) {
        int n;cin >> n;
        vi a(n + 1);for (int i = 1;i <= n;i++) cin >> a[i];
        sort(a.begin() + 1, a.end());
        int res = 0;
        vi cnt(N + 10);
        for (int i = 1;i <= n;i++) {
            for (auto x : factor[a[i]]) {
                res += cnt[x] * (n - i) * phi[x];
                cnt[x] += 1;
            }
        }
        cout << res << endl;
    }

}

法3.利用莫比乌斯反演的倍数形式

$\sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}\sum\limits_{x=1}^{N}x[gcd(a_i,a_j)=x]\cdot(n-j)$

$=\sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}\sum\limits_{x=1}^{N}x\sum\limits_{d=x,2x,...}[d|gcd(a_i,a_j)]\mu(\frac{d}{x})\cdot(n-j)$

$=\sum\limits_{x=1}^{N}x\sum\limits_{d=x,2x,...}\sum\limits_{i=1}^{n}[d|a_i]\sum\limits_{j=i+1}^{n}[d|a_j]\cdot(n-j)\mu(\frac{d}{x})$

令$f(d)=\sum\limits_{i=1}^{n}[d|a_i]\sum\limits_{j=i+1}^{n}[d|a_j]\cdot(n-j)$

原式$=\sum\limits_{x=1}^{N}x\sum\limits_{d=x,2x,...}f(d)\mu(\frac{d}{x})$

查看代码
 vvc<int> factor(N + 10);


void Solve(int TIME) {

    int T;cin >> T;
    for (int i = 1;i <= N;i++) {
        for (int j = 1;j * i <= N;j++) {
            factor[i * j].push_back(i);
        }
    }

    while (T--) {
        int n;cin >> n;
        vi a(n + 1);for (int i = 1;i <= n;i++) cin >> a[i];
        sort(a.begin() + 1, a.end());
        int res = 0;
        vi cnt(N + 10);
        vi f(N + 10);
        for (int i = 1;i <= n;i++) {
            for (auto x : factor[a[i]]) {
                f[x] += cnt[x] * (n - i);
                cnt[x] += 1;
            }
        }
        for (int i = 1;i <= N;i++) {
            for (int j = 1;i * j <= N;j++) {
                res += i * f[i * j] * mu[j];
            }
        }
        cout << res << endl;
    }

}

 

12.I-hx的数列

知识点

数论分块,莫比乌斯反演

题解

$(a,a\cdot \frac{c}{b},a\cdot{(\frac{c}{b})}^2)$,令$t=a\cdot (\frac{c}{b})^2$,需要$t\le n$

保证$gcd(b,c)=1$且$b<c$,于是就有$b^2|a$,这样才能有$t$为整数

于是令$k=\frac{a}{b^2}$,则有$k\cdot c^2 \le n$,于是有$k\le \lfloor\frac{n}{c^2}\rfloor$

$\sum\limits_{c=1}^{\lfloor\sqrt{n}\rfloor}\lfloor\frac{n}{c^2}\rfloor \sum\limits_{b=1}^{c-1}[\gcd(b,c)=1]=\sum\limits_{c=1}^{\lfloor\sqrt{n}\rfloor}\lfloor\frac{n}{c^2}\rfloor \sum\limits_{b=1}^{c-1}\sum\limits_{D|gcd(b,c)}\mu(D)$

$=\sum\limits_{Dc=1}^{\lfloor\sqrt{n}\rfloor}\lfloor\frac{n}{D^2c^2}\rfloor \sum\limits_{Db=1}^{Dc-1}\sum\limits_{D=1}^{\lfloor\sqrt{n}\rfloor}\mu(D)[D|gcd(Db,Dc)]$

$=\sum\limits_{Dc=1}^{\lfloor\sqrt{n}\rfloor}\lfloor\frac{n}{D^2c^2}\rfloor \sum\limits_{Db=1}^{Dc-1}\sum\limits_{D=1}^{\lfloor\sqrt{n}\rfloor}\mu(D)=\sum\limits_{D=1}^{\lfloor\sqrt{n}\rfloor}\mu(D)\sum\limits_{c=1}^{\lfloor\frac{\lfloor\sqrt{n}\rfloor}{D}\rfloor}\lfloor\frac{n}{D^2c^2}\rfloor \sum\limits_{b=1}^{c-1}1$

$=\sum\limits_{D=1}^{\lfloor\sqrt{n}\rfloor}\mu(D)\sum\limits_{c=1}^{\lfloor\frac{\lfloor\sqrt{n}\rfloor}{D}\rfloor}\lfloor\frac{n}{D^2c^2}\rfloor (c-1)$

 

复杂度$O(\sqrt1+\sqrt2+...+\sqrt{\sqrt{n}})$

大抵是不会很大的。

查看代码
 void Solve(int TIME) {

    int n;cin >> n;
    int res = 0;
    int sqn = sqrt(n);
    for (int i = 1;i <= sqn - 1;i++) {
        int t = 0;
        for (int l = 1, r;l <= sqn / i;l = r + 1) {
            int m = n / i / i;
            r = sqn / i;
            if (m / (l * l)) r = min((int)sqrt(m / (m / (l * l))), r);
            t += m / (l * l) * (l + r - 2) * (r - l + 1) / 2;
        }
        res += t * mu[i];
    }
    cout << res << endl;

}

 

13.牢大与GCD

知识点

莫比乌斯容斥

题意

给出长度为$n$的数组$a$和$num$,求下面的式子。

$\sum\limits_{x=1}^{n}\sum\limits_{k_1=1}^{n}\sum\limits_{k_2=1}^{n}...\sum\limits_{k_{num[x]}=1}^{n}[\gcd\limits_{i=1}^{num[x]}(a_{k_i})=x]$

题解

$\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\sum\limits_{k=1}^{n}[gcd(a_i,a_j,a_k)=x]$

$=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\sum\limits_{k=1}^{n}\sum\limits_{d=x,2x,...}[d|gcd(a_i,a_j,a_k)]\mu(\frac{d}{x})$

$=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}\sum\limits_{k=1}^{n}\sum\limits_{d=x,2x,...}[d|a_i][d|a_j][d|a_k]\mu(\frac{d}{x})$

$=\sum\limits_{d=x,2x,...}\mu(\frac{d}{x})\sum\limits_{i=1}^{n}[d|a_i]\sum\limits_{j=1}^{n}[d|a_j]\sum\limits_{k=1}^{n}[d|a_k]$

$=\sum\limits_{d=x,2x,...}\mu(\frac{d}{x})(\sum\limits_{i=1}^{n}[d|a_i])^3$

 

可以看出,当需要gcd的个数扩大至$X$,变的仅仅是后面的幂次。

于是令$f(d)=\sum\limits_{i=1}^{n}[d|a_i]$

结果为$\sum\limits_{x=1}^{n}\sum\limits_{d=x,2x,...}\mu(\frac{d}{x})f(d)^{pw[x]}$

查看代码
void Solve(int TIME) {

    int n;cin >> n;
    vi a(n + 1);for (int i = 1;i <= n;i++) cin >> a[i];
    int M = 1e5;
    vi cnt(M + 1);for (int i = 1;i <= n;i++) cnt[a[i]] += 1;

    //正解
    vi pw(n + 1);for (int i = 1;i <= n;i++) cin >> pw[i];
    vi f(M + 1);
    for (int i = 1;i <= M;i++) {
        for (int j = 1;j * i <= M;j++) {
            f[i] += cnt[i * j];
        }
    }
    int res = 0;
    for (int i = 1;i <= n;i++) {
        for (int j = 1;j * i <= M;j++) {
            res += mu[j] * qp(f[i * j], pw[i]);
        }
    }
    cout << res << endl;

    //暴力对拍
    // int res = 0;
    // for (int i = 1;i <= n;i++) {
    //     map<int, int> mp;mp[0] = 1;
    //     for (int j = 1;j <= pw[i];j++) {
    //         map<int,int> nmp;
    //         for (int k = 1;k <= n;k++) {
    //             for (auto [o1, o2] : mp) {
    //                 nmp[gcd(o1, a[k])] += o2;
    //             }
    //         }
    //         mp = nmp;
    //     }
    //     res += mp[i];
    // }
    // cout << res << endl;

}

 

14.G-小苯的逆序对_牛客小白月赛87 (nowcoder.com)

知识点

莫比乌斯容斥及其dp方法

题解

令$f(x)$表示$gcd(a_i,a_j)=x$并且$i和j$是逆序对的对数。

令$g(x)=f(x)+f(2x)+f(3x)...$,那么$f(x)=g(x)-(f(2x)+f(3x)+...)$ 。这里可以用dp做。

$f(x)=g(x)\mu(1)+g(2x)\mu(2)+...+g(kx)\mu(k)+...$ ,这里转换成莫比乌斯容斥。

$g(x)$是好算的,树状数组维护即可,做完了。

dp:

查看代码
 void Solve(int TIME) {
 
    int n;cin >> n;
    vi a(n + 1);
    vi pos(n + 1);
    for (int i = 1;i <= n;i++) cin >> a[i], pos[a[i]] = i;
    BIT tr(n + 1);
    vi f(n + 1);
    for (int i = n;i >= 1;i--) {
        for (int j = i;j <= n;j += i) {
            f[i] += tr.query(pos[j], n);
            tr.add(pos[j], 1);
        }
        for (int j = i;j <= n;j += i) {
            tr.add(pos[j], -1);
        }
        for (int j = 2 * i;j <= n;j += i) {
            f[i] -= f[j];
        }
    }
    cout << f[1] << endl;
 
}

莫比乌斯容斥:

查看代码
 void Solve(int TIME) {
 
    int n;cin >> n;
    vi a(n + 1);
    vi pos(n + 1);
    for (int i = 1;i <= n;i++) cin >> a[i], pos[a[i]] = i;
    BIT tr(n + 1);
    int res = 0;
    for (int i = 1;i <= n;i++) {
        int t = 0;
        for (int j = i;j <= n;j += i) {
            t += tr.query(pos[j], n);
            tr.add(pos[j], 1);
        }
        for (int j = i;j <= n;j += i) {
            tr.add(pos[j], -1);
        }
        res += t * mu[i];
    }
    cout << res << endl;
 
}

 

15.P2398 GCD SUM

知识点

莫比乌斯反演

题解

莫比乌斯反演

$\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}gcd(i,j)=\sum\limits_{d=1}^{n}d\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[gcd(i,j)=d]=\sum\limits_{d=1}^{n}d\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{n}{d}\rfloor}[gcd(di,dj)=d]$

$=\sum\limits_{d=1}^{n}d\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{n}{d}\rfloor}[gcd(i,j)=1]=\sum\limits_{d=1}^{n}d\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{D|gcd(i,j)}\mu(D)$

$=\sum\limits_{d=1}^{n}d\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{D=1}^{\lfloor\frac{n}{d}\rfloor}\mu(D)[D|gcd(i,j)]=\sum\limits_{d=1}^{n}d\sum\limits_{D=1}^{\lfloor\frac{n}{d}\rfloor}\mu(D)\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}[D|i]\sum\limits_{j=1}^{\lfloor\frac{n}{d}\rfloor}[D|j]$

$=\sum\limits_{d=1}^{n}d\sum\limits_{D=1}^{\lfloor\frac{n}{d}\rfloor}\mu(D)(\lfloor\frac{n}{Dd}\rfloor)^2$

查看代码
 void Solve(int TIME) {

    int n;cin >> n;
    int res = 0;
    for (int i = 1;i <= n;i++) {
        int m = n / i;
        for (int l = 1, r;l <= m;l = r + 1) {
            r = m;
            if (m / l) r = min(m / (m / l), r);
            res += (pre_mu[r] - pre_mu[l - 1]) * i * (m / l) * (m / l);
        }
    }
    cout << res << endl;

}

 

莫比乌斯容斥

$\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}gcd(i,j)=\sum\limits_{d=1}^{n}d\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[gcd(i,j)=d]$

令$g(d)=\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[gcd(i,j)=d]+\sum\limits_{i=1}^{n}\sum\limits_{j=1}^{n}[gcd(i,j)=2d]+...$

得到$\sum\limits_{d=1}^{n}d\sum\limits_{k=1}^{n}\mu(k)g(kd)$

$g(x)$很好算,即每个$x$的倍数都可以选,选两个可以重复。即$(\lfloor\frac{n}{x} \rfloor)^2$

查看代码
 void Solve(int TIME) {

    int n;cin >> n;
    vi g(n + 1);
    for (int i = 1;i <= n;i++) {
        g[i] = (n / i) * (n / i);
    }
    int res = 0;
    for (int i = 1;i <= n;i++) {
        for (int j = 1;j * i <= n;j++) {
            res += i * mu[j] * g[j * i];
        }
    }
    cout << res << endl;

}

 

数论与杂项

1. 51nod1236 - 序列求和 V3

知识点

特征方程,二次剩余

题解

$F[n]=F[n-1]+F[n-2]$ ,由特征方程 $q^2=q+1$,得到特征根$q_1=\frac{1+\sqrt5}{2}$ ,$q_2=\frac{1-\sqrt5}{2}$

则有$F[0]=k_1q_1^0+k_2q_2^0=0$ ,$F[1]=k_1q_1^1+k_2q_2^1=1$,

解得$k_1=\frac{1}{\sqrt5}$ , $k_2=-\frac{1}{\sqrt5}$ , 得到 $F[n]=\frac{1}{\sqrt5}(\frac{1+\sqrt5}{2})^n-\frac{1}{\sqrt5}(\frac{1-\sqrt5}{2})^n=\frac{1}{\sqrt5}[(\frac{1+\sqrt5}{2})^n-(\frac{1-\sqrt5}{2})^n]$

$F[n]^k=(\frac{1}{\sqrt5})^k[(\frac{1+\sqrt5}{2})^n-(\frac{1-\sqrt5}{2})^n]^k =(\frac{1}{\sqrt5})^k\sum\limits_{i=0}^{k}C_{k}^{i}(\frac{1+\sqrt5}{2})^{ni}(\frac{1-\sqrt5}{2})^{n(k-i)}(-1)^{k-i}$

$S(n,k)=\sum\limits_{i=1}^{n}F[i]^k=\sum\limits_{i=1}^{n}(\frac{1}{\sqrt5})^k\sum\limits_{j=0}^{k}C_{k}^{j}(\frac{1+\sqrt5}{2})^{ij}(\frac{1-\sqrt5}{2})^{i(k-j)}(-1)^{k-j}$

$=(\frac{1}{\sqrt5})^k\sum\limits_{i=1}^{n}\sum\limits_{j=0}^{k}C_{k}^{j}[(\frac{1+\sqrt5}{2})^{j}(\frac{1-\sqrt5}{2})^{(k-j)}]^i(-1)^{k-j}$

$=(\frac{1}{\sqrt5})^k\sum\limits_{j=0}^{k}(-1)^{k-j}C_{k}^{j}\sum\limits_{i=1}^{n}[(\frac{1+\sqrt5}{2})^{j}(\frac{1-\sqrt5}{2})^{(k-j)}]^i$

后面的循环是个等比数列,快速幂处理即可,总时间复杂度$O(klogn)$

$\sqrt5$在模意义下的值用二次剩余得到 ,可以直接打表找或使用Cipolla算法。在本题中可以得到 383008016是5的一个二次剩余。

查看代码
void Solve(int TIME) {

    int n, k;cin >> n >> k;
    int res = 0;
    int a = (1 + sqrt5) % MOD * inv(2) % MOD;
    int b = (MOD + 1 - sqrt5) % MOD * inv(2) % MOD;
    for (int j = 0;j <= k;j++) {
        int t = qp(a, j) * qp(b, k - j) % MOD;
        int tt;
        if (t != 1) tt = t * (qp(t, n) - 1) % MOD * inv(t - 1) % MOD;
        else tt = n % MOD;
        if ((k - j) & 1) {
            res = sub(res, mul(comb(k, j), tt));
        }
        else {
            res = add(res, mul(comb(k, j), tt));
        }
    }
    res = mul(res, qp(inv(sqrt5), k));
    cout << norm(res) << endl;

}

 

2.CF1891D - Suspicious logarithms 

知识点

数论分块

题解

$2^{f(x)}\le x$,则有$f(x)=\lfloor log_2x \rfloor$

$f(x)^{g(x)}\le x$,则有$g(x)\cdot log_2f(x)\le log_2x$,即$g(x)\cdot log_2{\lfloor log_2x \rfloor}\le log_2x$,即$g(x)=\lfloor \frac{log_2x}{log_2{\lfloor log_2x \rfloor}} \rfloor$,即求$\sum\limits_{i=l}^{r}g(i)$

对于相等的$\lfloor log_2x \rfloor$,当$x$增大,$g(x)$也增大。而$\lfloor log_2x \rfloor$的范围比较小,考虑枚举。之后在该范围,将其划分为多个不同的连续相同数字子段。

注意:$log_2(x)$这类函数默认使用$double$类型,使用前要强制转换为$long\; double$,否则会爆。

查看代码
 int f(int x) {
    return 63 - __builtin_clzll(x);
}
int g(int x) {
    return (ll)(log2(ld(x)) / log2((ld)f(x)));
}

struct qwq {
    int l, r, v;
};

void Solve(int TIME) {

    vc<qwq> a;int L = 4, R = 1E18;
    for (int l = L, r;l <= R;l = r + 1) {
        int fx = f(l);int lim = min((1ll << (fx + 1)) - 1, R);
        int lo = l + 1, hi = lim;
        int gx = g(l);
        while (lo <= hi) {
            int mid = (lo + hi) >> 1;
            if (g(mid) <= gx) {
                lo = mid + 1;
            }
            else {
                hi = mid - 1;
            }
        }
        r = lo - 1;
        a.push_back({ l,r,gx });
    }

    int q;cin >> q;
    while (q--) {
        cin >> L >> R;
        int res = 0;
        auto pt = partition_point(a.begin(), a.end(), [&](qwq A) {return A.r < L;}) - a.begin();
        for (int i = pt;i < a.size();i++) {
            auto [l, r, v] = a[i];
            if (l > R) break;
            else {
                l = max(l, L), r = min(r, R);
                res = (res + (r - l + 1) * v % MOD) % MOD;
            }
        }
        cout << res << endl;
    }
}

 

3.CFgym104076 E - Identical Parity

知识点

exgcd

题解

发现最后$1,1+k,1+2k...$ ,     $2,2+k,2+2k...$,      ...    $k-1,k-1+k,k-1+2k,....$这些都是同奇偶性。

也就是分成$k$组,其中$n \;mod \;k$组是$\lfloor\frac{n}{k}\rfloor+1$个元素,剩下$k-n \;mod\;k$组是$\lfloor\frac{n}{k}\rfloor$个元素。令$a=\lfloor\frac{n}{k}\rfloor$

然后要使得$ax+(a+1)y=n/2$有解$x,y$使得$0\le x\le (n \;mod\;k)$ 并且$0\le y \le (k-n \;mod\;k) $

二分求出每个边界的可行范围看有无交集即可。

查看代码
 void Solve(int TIME) {

    int n, k;cin >> n >> k;
    int a = n / k;//人数
    int t = n % k;//人数为a+1的组数t,人数为a的组数k-t
    int x, y;
    int d = exgcd(a, a + 1, x, y);
    int m = n / 2;
    if (m % d) return NO, void();
    int base = m / d;
    x *= base, y *= base;
    int norm_1 = a + 1;
    int norm_2 = a;

    int l, r;
    l = -1e9, r = 1e9;
    while (l <= r) {
        int mid = l + r >> 1;
        if (x + mid * (a + 1) <= k - t) l = mid + 1;
        else r = mid - 1;
    }
    int r_a = l - 1;

    l = -1e9, r = 1e9;
    while (l <= r) {
        int mid = l + r >> 1;
        if (x + mid * (a + 1) >= 0) r = mid - 1;
        else l = mid + 1;
    }
    int l_a = r + 1;

    l = -1e9, r = 1e9;
    while (l <= r) {
        int mid = l + r >> 1;
        if (y - mid * (a) <= t) r = mid - 1;
        else l = mid + 1;
    }
    int l_b = r + 1;

    l = -1e9, r = 1e9;
    while (l <= r) {
        int mid = l + r >> 1;
        if (y - mid * (a) >= 0) l = mid + 1;
        else r = mid - 1;
    }
    int r_b = l - 1;

    if (min(r_a, r_b) >= max(l_a, l_b)) YES;
    else NO;

}

 

4.2023ICPC区域赛南京站C

知识点

异或的性质

题意

求满足 $ i \text{^}  (P-1)\equiv 1 (mod\; P)$  并且$0\le i\le m$的$i$的个数

范围$1\le m \le 10^{18} $,$1\le P \le 10^{18}$

题解

即 $i\text{^} (P-1)= kP+1$ ,$(k\ge 0)$ ,即$i=(kP+1) \text{^}  (P-1)\le m$的数量

我们知道异或的性质$a-b \le $ $a \text{^}  b$ $\le a+b$

所以$(k-1)P+2\le(kP+1) \text{^}  (P-1) \le(k+1)P$

当$k\ge \lceil\frac{m}{p}\rceil+1$ ,$(kP+1) \text{^}  (P-1) \ge(k-1)P+2 > m$ 

当$0\le k\le \lfloor\frac{m}{p}\rfloor-1$ ,$(kP+1) \text{^}  (P-1) \le(k+1)P \le m$

所以只需检查$\lfloor\frac{m}{p}\rfloor \le k\le \lceil\frac{m}{p} \rceil$范围的$k$是否满足$(kP+1)\text{^}  (P-1)\le m$即可

查看代码
 void Solve(int TIME) {

    int P, m;cin >> P >> m;
    int res = m / P;
    for (int i = m / P;i <= (m + P - 1) / P;i++) {
        int t = (i * P + 1) ^ (P - 1);
        if (t <= m) res += 1;
    }
    cout << res << endl;

}

好像不需要卡这么死,知道大致上界后往后枚举个10位就行了。

查看代码
 void Solve(int TIME) {

    const __int128 ONE = 1;
    int p, m;cin >> p >> m;
    int res = m / p;
    int n = m / p - 1;
    for (int i = n + 1;i <= n + 10;i++) {
        __int128 t = ONE * (ONE * i * p + 1) ^ (ONE * p - 1);
        if (t <= m) res += 1;
    }
    cout << res << endl;

}

 

解2

 根据上面的式子,将$m$拆位枚举,枚举公共前缀,在$1$的位置替换为$0$,然后后面的可以为任意。之后用类似前缀和的方式算一下答案。

查看代码
 void Solve(int TIME) {

    int p, m;cin >> p >> m;
    int mask = 0;
    int res = 0;
    auto calc = [&](int x) {//[0,x]中满足kp+1=x的个数
        x--;
        if (x < 0) return 0ll;
        return x / p + 1;
        };
    auto calc2 = [&](int l, int r) {//[l,r]中满足
        return calc(r) - calc(l - 1);
        };
    for (int i = 60;i >= 0;i--) {
        if (~m >> i & 1) continue;
        int t = mask ^ (p - 1);
        int l = t >> i << i, r = l + (1ll << i) - 1;
        res += calc2(l, r);
        mask += 1ll << i;
    }
    res += calc2(mask ^ (p - 1), mask ^ (p - 1));
    cout << res << endl;

}

 

5.2019ICPC西安站 F - Function!

知识点

数论分块

题意

$\sum\limits_{a=2}^{n}(a\sum\limits_{b=a}^{n}\lfloor log_ab \rfloor)$

题解

$\sum\limits_{a=2}^{n}(a\sum\limits_{b=a}^{n}\lfloor log_ab \rfloor \lceil log_ba\rceil)=\sum\limits_{a=2}^{n}(a\sum\limits_{b=a}^{n}\lfloor log_ab \rfloor)$

发现当$a\cdot a > n$,$\lfloor log_ab \rfloor =1$ ,此时有$\sum\limits_{a=\lfloor\sqrt{n}\rfloor+1}^{n}a(n-a+1)=\sum\limits_{a=\lfloor\sqrt{n}\rfloor+1}^{n}\left(a(n+1)-a^2\right)$

于是在$a\cdot a \le n$时,分块计算,$\sum\limits_{a=2}^{\lfloor \sqrt{n} \rfloor}\sum\limits_{b=a}^{n}\lfloor log_ab \rfloor$

复杂度$O(log1+log2+...+log\sqrt{n})=O(log{\frac{(1+\sqrt{n})\sqrt{n}}{2}})=O(logn)$

查看代码
 int inv6 = inv(6);
int inv2 = inv(2);

int calc_n(int x) {
    x %= MOD;
    return (1 + x) % MOD * x % MOD * inv2 % MOD;
}
int calc_n_2(int x) {
    x %= MOD;
    return (2 * x + 1) % MOD * (x + 1) % MOD * x % MOD * inv6 % MOD;
}

void Solve(int TIME) {

    int n;cin >> n;
    int res = 0;
    int a = 2;
    for (a = 2;a * a <= n;a++) {
        for (int l = a, r, k = 1;l <= n;l = r + 1, k++) {
            r = min(l * a - 1, n);
            res = (res + (r - l + 1) % MOD * k % MOD * a % MOD) % MOD;
        }
    }
    res = (res + (n + 1) % MOD * (calc_n(n) - calc_n(a - 1)) % MOD) % MOD;
    res = (res - (calc_n_2(n) - calc_n_2(a - 1)) % MOD) % MOD;
    res = norm(res);
    cout << res << endl;

}

 

6.P5110 块速递推

知识点

二次剩余,特征方程,光速幂

题解

$a_n=233a_{n-1}+666a_{n-2}$ ,特征方程$q^2=233q+666$

得到$\begin{cases} q_1=\frac{233+\sqrt{56953}}{2} \\q_2= \frac{233-\sqrt{56953}}{2} \end{cases}$

通项$a_n=k_1\cdot q_1^n+k_2\cdot q_2^n$,带入$a_0=k_1+k_2=0$ ,$a_1=k_1\cdot q_1+k_2\cdot q_2=1$

得到$k_1=\frac{1}{q_1-q_2}=\frac{1}{\sqrt{56953}}$,$k_2=-\frac{1}{\sqrt{56953}}$。则有$a_n=\frac {(\frac{233+\sqrt{56953}}{2})^n-(\frac{233-\sqrt{56953}}{2})^n}{\sqrt{56953}}$

发现$\sqrt{56953}$在$10^9+7$下的二次剩余为$188305837$,那么直接带入进去即可。

$a_n=\frac {(\frac{233+188305837}{2})^n-(\frac{233-188305837}{2})^n}{\sqrt{56953}}=\frac {94153035^n-(-94152802)^n}{188305837}=\frac {94153035^n-905847205^n}{188305837}(mod \;10^9+7) $ 

$=233230706\cdot(94153035^n-905847205^n)$

然后光速幂$O(\sqrt{MOD})$预处理,$O(1)$查询即可。总复杂度$O(T+\sqrt{MOD})$

 

查看代码
 struct LP {//光速幂,处理同底数同模数的幂
    int base1[N], basesqrt[N];
    int Block_len;
    int Phi;
    ll maxn = 1e10;//模数的最大值
    LP(int x) { init(x); }
    void init(int x) {//初始化底数为x
        Phi = MOD - 1;
        Block_len = sqrt(maxn) + 1;
        base1[0] = 1;for (int i = 1;i <= Block_len;i++) base1[i] = 1ll * base1[i - 1] * x % MOD;
        basesqrt[0] = 1;for (int i = 1;i <= Block_len;i++) basesqrt[i] = 1ll * basesqrt[i - 1] * base1[Block_len] % MOD;
    }
    int qp(ull x) {
        x %= Phi;
        return 1ll * basesqrt[x / Block_len] * base1[x % Block_len] % MOD;
    }
};

namespace Mker
{
    unsigned long long SA, SB, SC;
    void init() { cin >> SA >> SB >> SC; }
    unsigned long long rand()
    {
        SA ^= SA << 32, SA ^= SA >> 13, SA ^= SA << 1;
        unsigned long long t = SA;
        SA = SB, SB = SC, SC ^= t ^ SA;return SC;
    }
}

void Solve(int TIME) {

    int T;cin >> T;
    Mker::init();
    LP a(94153035);
    LP b(905847205);
    int res = 0;

    while (T--) {
        int n = Mker::rand();
        res ^= 233230706 * (a.qp(n) - b.qp(n) + MOD) % MOD;
    }
    cout << res << endl;

}

 

7.abc331D - Tile Pattern

知识点

容斥

题解

考虑对一个右下角$(x,y)$计算它到$(1,1)$的$1$的个数。

发现可以分为四个部分来求:

1.左上角被$(x/n)\cdot (y/n)$个$n\cdot n$覆盖的正方形区域;

2.左下角被$(y/n)$个$(x \mod n )\cdot n$覆盖的矩形区域

3.右上角被$(x/n)$个$x\cdot (y\mod n)$覆盖的矩形区域

4.右下角被$1$个$(x\mod n)\cdot (y\mod n)$覆盖的矩形区域

画图大点的图手动模拟一下可以容易得出这个结论,我用了$8\cdot 7$的矩形,$n=3$

然后容斥一下,用类似二位前缀和的方式,得到答案$res=calc(x_2,y_2)-calc(x_2,y_1-1)-calc(x_1-1,y_2)+calc(x_1-1,y_1-1)$

 

查看代码
 void Solve(int TIME) {

    int n, q;cin >> n >> q;
    vvc<int> g(n + 1, vc<int>(n + 1));
    for (int i = 1;i <= n;i++) {
        for (int j = 1;j <= n;j++) {
            char ch;cin >> ch;
            if (ch == 'B') g[i][j] = 1;
        }
    }

    vvc<int> sumG(n + 1, vc<int>(n + 1));
    for (int i = 1;i <= n;i++) {
        for (int j = 1;j <= n;j++) {
            sumG[i][j] = sumG[i][j - 1] + sumG[i - 1][j] - sumG[i - 1][j - 1] + g[i][j];
        }
    }

    auto calc = [&](int x, int y) {
        int res = 0;
        res += (x / n) * (y / n) * sumG[n][n];
        res += (y / n) * sumG[x % n][n];
        res += (x / n) * sumG[n][y % n];
        res += sumG[x % n][y % n];
        return res;
        };

    while (q--) {
        int x1, y1, x2, y2;cin >> x1 >> y1 >> x2 >> y2;
        x1 += 1, y1 += 1, x2 += 1, y2 += 1;
        cout << calc(x2, y2) - calc(x2, y1 - 1) - calc(x1 - 1, y2) + calc(x1 - 1, y1 - 1) << endl;
    }

}

 

8.青蛙的约会

知识点

exgcd

题解

$x+am=y+an+kL$

$a(m-n)-kL=y-x$

查看代码
 void Solve(int TIME) {

    int x, y, m, n, l;cin >> x >> y >> m >> n >> l;
    int a, k;
    int d = exgcd(m - n, -l, a, k);
    y -= x;
    if (y % d) return cout << "Impossible" << endl, void();
    int base = y / d;
    a *= base, k *= base;
    a = norm(a, l / gcd(l, m - n));
    cout << a << endl;

}

 

9.C-Prime Distance

知识点

线性筛,埃筛

题解

可以发现只需要用先线性筛预处理出$\sqrt{值域}$范围内的质数,然后对指定区间埃筛即可。

复杂度$O(nloglog{10^6})$

查看代码
 void Solve(int TIME) {

    int l, r;cin >> l >> r;
    vi v(r - l + 10);
    for (auto i : p) {
        if (i > r) continue;
        for (int j = l / i;j <= r / i;j++) {
            if (j == 1) continue;
            if (i * j >= l) v[j * i - l] = 1;
        }
    }
    if (l == 1) v[0] = 1;
    vi ans;
    for (int i = 0;i <= r - l;i++) {
        if (v[i] == 0) ans.push_back(i + l);
    }
    if (ans.size() < 2) cout << "There are no adjacent primes." << endl;
    else {
        int mn = inf;
        int res = 1;
        int mx = 0;
        int res2 = 1;
        for (int i = 1;i < ans.size();i++) {
            if (ans[i] - ans[i - 1] < mn) {
                mn = ans[i] - ans[i - 1];
                res = i;
            }
            if (ans[i] - ans[i - 1] > mx) {
                mx = ans[i] - ans[i - 1];
                res2 = i;
            }
        }
        cout << ans[res - 1] << "," << ans[res] << " are closest, ";
        cout << ans[res2 - 1] << "," << ans[res2] << " are most distant." << endl;

    }

}

 

10.X-factor Chains

知识点

多重集排列

题解

按唯一分解定理,将这个数字分解为若干质数的乘积,然后发现相同质因子之间可以交换顺序,其实就是个多重集排列。

查看代码
 void Solve(int TIME) {

    int x;cin >> x;
    int res = 0;
    vi ans;
    for (int i = 2;i <= x / i;i++) {
        int cnt = 0;
        while (x % i == 0) {
            res += 1;
            cnt += 1;
            x /= i;
        }
        if (cnt)  ans.push_back(cnt);
    }
    if (x > 1) res += 1, ans.push_back(1);

    vi fact(22);fact[0] = 1;
    for (int i = 1;i <= 20;i++) {
        fact[i] = fact[i - 1] * i;
    }
    debug(fact[20]);
    cout << res << " ";
    res = fact[res];
    for (auto i : ans) {
        res /= fact[i];
    }

    cout << res << endl;

}

 

11.CF1840G2 - In Search of Truth

知识点

meet in the middle

题解

G1

用类似BSGS的思想,其实也是meet in the middle的思想。或者更类似根号分治?

令$T=\lceil\sqrt{n}\rceil$,$i \in[1,T]$,$j\in[1,T]$ ,这样$T\cdot i-j$可以表示所有$[0,n-1]$的数。

想要得到答案,可以令$T\cdot i-j\equiv 0(\mod n)$,那么有$T\cdot i \equiv j (\mod n)$

先预处理出前$T$个数放入哈希表,然后再每次走$T$步看有没有和前$T$个数相同的数。

查看代码
 void Solve(int TIME) {

    vi vis(1e6 + 1, -1);
    int now;cin >> now;
    vis[now] = 0;
    auto add = [&](int x) {
        cout << "+ " << x << endl;
        cin >> now;
        };
    auto answ = [&](int x) {
        cout << "! " << x << endl;
        };
    int res = now;
    for (int i = 1;i <= 1000;i++) {
        add(1);
        if (vis[now] != -1) {
            answ(res);
            return;
        }
        res = max(res, now);
        vis[now] = i;
    }

    for (int i = 1;;i++) {
        add(1000);
        if (vis[now] != -1) {
            answ((i + 1) * 1000 - vis[now]);
            return;
        }
    }

}

G2

 还是看不懂啊,明年再来看吧

类似题 2022区域赛杭州站I - Guess Cycle Length

 

12.K-寂寞沙洲冷

知识点

01分数规划

题解

$k$个生成树,也就是一开始是$n$棵树(连通块),每次连边都会减少一个连通块,于是最后变成k个的时候退出就可以了。

然后边权就是典型的01分数规划求比率生成树。注意这里求最大生成树。

Dinkelbach方法
 void Solve(int TIME) {

    int n, m, k;cin >> n >> m >> k;
    vc<ai4> edge;
    for (int i = 1;i <= m;i++) {
        int x, y, a, b;cin >> x >> y >> a >> b;
        edge.push_back({ x,y,a,b });
    }
    const ld eps = 1e-8;
    ld ans = eps;
    while (1) {
        DSU d(n + 1);
        vc<pr<ld, ai4>> g;
        for (auto [x, y, a, b] : edge) {
            g.push_back({ -a + b * ans,{x,y,a,b} });
        }
        sort(g.begin(), g.end());
        ld res = 0;int now = n;
        int A = 0, B = 0;
        for (auto [w, u] : g) {
            if (d.find(u[0]) != d.find(u[1])) {
                d.merge(u[0], u[1]);
                A += u[2];
                B += u[3];
                now -= 1;
                if (now == k) break;
            }
        }
        ld t = 1.0 * A / B;
        if (abs(t - ans) < eps) break;
        ans = t;
    }
    cout << ans << endl;

}

 

二分方法
 void Solve(int TIME) {
 
    int n, m, k;cin >> n >> m >> k;
    vc<ai4> edge;
    for (int i = 1;i <= m;i++) {
        int x, y, a, b;cin >> x >> y >> a >> b;
        edge.push_back({ x,y,a,b });
    }
    const ld eps = 1e-8;
    ld l = eps, r = 1e4 + 10;
    while (r - l > eps) {
        DSU d(n + 1);
        ld mid = (l + r) / 2;
        vc<pr<ld, ai2>> g;
        for (auto [x, y, a, b] : edge) {
            g.push_back({ -a + b * mid,{x,y} });
        }
        sort(g.begin(), g.end());
        ld res = 0;int now = n;
        for (auto [w, u] : g) {
            if (d.find(u[0]) != d.find(u[1])) {
                d.merge(u[0], u[1]);
                res += -w;
                now -= 1;
                if (now == k) break;
            }
        }
        if (res > eps) l = mid;
        else r = mid;
    }
    cout << l << endl;
 
}

二分慢了4~5倍。 

 

13.G-scx的记忆碎片

知识点

dp,组合数

题解

dp求出用1~9组成w位,总和为k的方案数。然后组合数搞一下。

注意这里m很大,组合数需要特殊处理。

注意特判k=1的情况。

查看代码
 int comb(int a, int b, int mod = MOD) {//C(a,b)=A(a,b)/b!
    if (b < 0 || a < 0 || b > a) return 0;
    int res = 1;
    for (int i = a;i >= a - b + 1;i--) res = i % mod * res % mod;
    res = res * infact[b] % mod;
    return res;
}
void Solve(int TIME) {

    int m, k;cin >> m >> k;
    int W = min(100ll, m);
    vvc<int> f(W + 1, vi(k + 1));
    f[0][0] = 1;
    for (int w = 1;w <= W;w++) {
        for (int i = 0;i <= k;i++) {
            for (int j = 1;j <= 9;j++) {
                if (i >= j) f[w][i] = (f[w][i] + f[w - 1][i - j]) % MOD;
            }
        }
    }

    int res = 0;

    for (int i = 0;i <= W;i++) {
        int cnt = f[i][k];
        res = (res + cnt * comb(m, i) % MOD) % MOD;
    }
    if (k == 1) res = (res + 1) % MOD;
    cout << res << endl;

}

 

14.5.数学尖子生

知识点

容斥

题解

容易推知,如果一个数$x$被$lcm(1,2,...n-1)$整除,但是不被$lcm(1,2,...n)$整除,那么这个数的$f(x)=n$。

于是预处理$Lcm$,然后容斥即可。

查看代码
 void Solve(int TIME) {

    int lc = 1;
    vi Lcm(50);
    for (int i = 1;i <= 42;i++) {
        lc = lcm(lc, i);
        Lcm[i] = lc;
    }
    int t;cin >> t;
    while (t--) {
        int a, n;cin >> a >> n;
        if (a >= 42 || a == 1) {
            cout << 0 << endl;
            continue;
        }
        int res = n / Lcm[a - 1] - n / Lcm[a];
        if (res <= 0) cout << 0 << endl;
        else cout << res << endl;
    }

}

 

15.4.取余 

知识点

题解

如果范围小一点,可以直接数论分块$O(a\sqrt {b})$做,但是这里范围比较大所以考虑换其他方法。

考虑枚举$b$,这样每个合法的数的范围是$[bx+l,bx+r]$,其中$l=s$,$r=min(b-1,t)$,x=0,1,2...

然后根据这个范围计算一下有几个数符合条件,需要特判$l=0$和最后一个块的东西。不得不说调特判的能力是非常重要的!

查看代码
 void Solve(int TIME) {

    int A, B, s, t;cin >> A >> B >> s >> t;
    int res = 0;
    for (int b = 1;b <= B;b++) {
        if (b <= s) continue;
        int l = s, r = min(b - 1, t);
        int x = A / b;
        res += (r - l + 1) * x;
        if (l == 0) res -= 1;
        int t = A - b * x;
        if (t < l) continue;
        res += min(t, r) - l + 1;
    }
    cout << res << endl;

}

 

16.CF1861E - Non-Intersecting Subpermutations

知识点

容斥,划分型dp

题解

法1.

显然有个贪心的策略,应该尽量选择靠前的合法串。

考虑贡献的思想,以每个位置为结尾的合法段都求出贡献然后累加。

设$f_i$表示前$i$个位置中,最后一个长度为$k$的段以第$i$个位置为结尾的贡献次数。

显然可以先将$f_i=k!\cdot k^{i-k}$,然后减去不合法的方案。

于是不合法等价于,存在一个$j\in[i-k+1,i-1]$,使得$j$位置为结尾存在一个合法段。

于是方案数就是$f_j \cdot (i-j)!$,$(i-j)!$表示剩下的数字任意次序。

于是有$f_i=k!\cdot k^{i-k}-\sum\limits_{j=i-k+1}^{i-1}f_j \cdot(i-j)!$,后面的$n-i$个数随便选。

于是答案为$\sum\limits_{i=1}^{n}f_i\cdot k^{n-i}$

查看代码
 void Solve(int TIME) {
 
    int n, k;cin >> n >> k;
    vi f(n + 1);
    int res = 0;
    for (int i = k;i <= n;i++) {
        int t = 0;
        for (int j = i - k + 1;j <= i - 1;j++) {
            t = Add(t, mul(f[j], fact[i - j]));
        }
        f[i] = Dec(mul(fact[k], qp(k, i - k)), t);
        res = Add(res, mul(f[i], qp(k, n - i)));
    }
    cout << res << endl;
 
}

 

法2.

从前往后扫,如果找到了一个长度为$k$的且满足条件的段就选择。

所以可以想到一个$dp$,设$f_{i,j}$表示前$i$个位置,末尾最长的,值只出现一次的子数组。

$f_{i+1,j+1} += f_{i,j}\cdot(k-j)$,$(i+1不是[i-j+1,i]出现的值)$

$f_{i+1,j} += f_{i,t}$ ,$t\in[j,k-1] $ ,$(i+1是[i-j+1,i]出现的值)$

$f_{i+1,0}=f_{i+1,k}$,插入一个值后所有的值都出现了,让长度归$0$重新开始。

于是答案就是$\sum\limits_{i=1}^{n}f_{i,k}\cdot k^{n-i}$

其实也用了贡献的思想。不过这里直接dp求出了上面法1容斥求出的东西。

查看代码
 void Solve(int TIME) {

    int n, k;cin >> n >> k;
    vvc<int> f(n + 1, vi(k + 1));
    f[0][0] = 1;
    for (int i = 0;i < n;i++) {
        for (int j = 0;j < k;j++) {
            f[i + 1][j + 1] = Add(f[i + 1][j + 1], mul(f[i][j], (k - j)));
        }
        int s = 0;
        for (int j = k - 1;j >= 1;j--) {
            s = Add(s, f[i][j]);
            f[i + 1][j] = Add(f[i + 1][j], s);
        }
        f[i + 1][0] = f[i + 1][k];
    }
    int res = 0;
    for (int i = 1;i <= n;i++) {
        res = Add(res, mul(f[i][k], qp(k, n - i)));
    }
    cout << res << endl;

}

 

17.CF1790E - Vlad and a Pair of Numbers

考虑把$a+b$换成位运算符号,才方便构造。

$a\text{^} =a|b-a\&b=x$

$a+b=a|b+a\&b=2x$

得 $a\&b=\frac{x}{2}$ ,又根据$a\text{^}  b=x$

可以很容易构造解,分别看$x$和$y=\frac{x}{2}$的每一位数,如果$\{x,y\}=\{0,1\}$则$\{a,b\}=\{1,1\}$;如果$\{x,y\}=\{1,0\}$则$\{a,b\}=\{1,0\}或\{0,1\}$;如果$\{x,y\}=\{0,0\}$则$\{a,b\}=\{0,0\}$;如果$\{x,y\}=\{1,1\}$则无解。根据这个写一下即可。

 

查看代码
 void Solve(int TIME) {
 
    int x;cin >> x;
    if (x & 1) return cout << -1 << endl, void();
    int y = x / 2;
    int a = 0, b = 0;
    for (int i = 30;i >= 0;i--) {
        int u = x >> i & 1;
        int v = y >> i & 1;
        if (u == 1 && v == 1) return cout << -1 << endl, void();
        else if (u == 1 && v == 0) a += 1 << i;
        else if (u == 0 && v == 1) a += 1 << i, b += 1 << i;
    }
    cout << a << " " << b << endl;
 
}

 

期望与概率

1. UVA12004 Bubble Sort

知识点

贡献计算

题解

考虑每个有序对对答案的贡献$f(i,j)$,其中$i<j$

$res=\sum\limits_{i=1}^{n}\sum\limits_{j=i+1}^{n}f(i,j)$。而$f(i,j)=1时$,$i$在$j$的后面。 $f(i,j)=0$时,i在j的前面。这两种情况的概率都是$\frac{1}{2}$

即$f(i,j)=\frac{1}{2}$, 那么$res=\frac{n(n+1)}{2}\cdot \frac{1}{2}=\frac{n(n+1)}{4}$

查看代码
 void Solve(int TIME) {

    int n;
    cin >> n;
    ll res = 1ll * n * (n - 1) / 2;
    cout << "Case " << TIME << ": ";
    if (res & 1) {
        cout << res << "/" << 2 << endl;
    }
    else {
        cout << res / 2 << endl;
    }

}

 

2.ABC326E - Revenge of "The Salary of AtCoder Inc."

知识点 期望dp   题解   期望dp考虑逆推。

设状态$f_i$表示当前的$x$为$i$时,终止时获得钱的期望数量

显然有$f_n=0$,因为此时无论抽中什么都会中终止。

$f_{n-1}=\frac{a_n}{n}$,即抽中$n$有贡献,否则终止。

$f_{n-2}=\frac{a_{n-1}+f_{n-1}}{n}+\frac{a_{n}}{n}$

$f_{n-3}=\frac{a_{n-2}+f_{n-2}}{n}+\frac{a_{n-1}+f_{n-1}}{n}+\frac{a_n}{n}$

然后发现$f_n=f_{n+1}+\frac{a_{n+1}+f_{n+1}}{n}$,倒着递推即可。

查看代码
void Solve(int TIME) {

    int n;cin >> n;
    vi a(n + 1);for (int i = 1;i <= n;i++) cin >> a[i];
    int invn = inv(n);
    vi f(n + 1);//目前x=i,至结束获得钱的期望
    //f[n-1]=a[n]/n+0,f[n-2]=(a[n-1]+f[n-1])/n+a[n]/n,f[n-3]=(a[n-2]+f[n-2])/n+(a[n-1]+f[n-1])n+(a[n])/n
    f[n] = 0;
    for (int i = n - 1;i >= 0;i--) {
        f[i] = (f[i + 1] + (a[i + 1] + f[i + 1]) % MOD * invn % MOD) % MOD;
    }
    cout << f[0] << endl;

}

 

3.nc68504M - 让二追三

知识点

贡献计算期望

题解

考虑计算每个长度为5的连续子段的对答案贡献的期望,然后答案就是所有的这些单个的累加,即$(n-4)$个的累加,即$(n-4)\cdot (1-p)(1-p)(1-p)\cdot p\cdot p $

查看代码
 void Solve(int TIME) {

    int a, b, n;cin >> a >> b >> n;
    int p = a * inv(b) % MOD;
    if (n <= 4) return cout << 0 << endl, void();
    int res = (n - 4) * (1 - p + MOD) % MOD * (1 - p + MOD) % MOD * p % MOD * p % MOD * p % MOD;
    cout << res << endl;

}

 

4.牛客挑战赛71 - A 和的期望

知识点

贡献计算期望

题解

仅考虑单个数字对答案的贡献,如果要选出$i$个数字,那么就需要在选出这个数字后,再在剩下的$n-1$个数字选出$i-1$个。而总方案数是$C_{n}^{i}$,所以答案是$\frac{C_{n-1}^{i-1}S}{C_{n}^{i}}=\frac{(n-1)!}{(i-1)!(n-i)!} \frac{i!(n-i)!}{n!}S=\frac{i}{n}S$,其中$S=\sum\limits_{i=1}^{n}a_i$

查看代码
 void Solve(int TIME) {

    int n;cin >> n;
    vi a(n + 1);for (int i = 1;i <= n;i++) cin >> a[i];

    int s = accumulate(a.begin() + 1, a.end(), 0ll);
    s %= MOD;

    for (int i = 1;i <= n;i++) {
        cout << s * i % MOD * inv(n) % MOD << " ";
    }
    cout << endl;
}

 

5.abc332F - Random Update Query

知识点

计算期望

题解

发现每次对于一个区间里的任意一个数,设它的之前的期望是$E$。它的新的期望是$\frac{(r-l)\cdot E}{r-l+1}+\frac{x}{r-l+1}$

维护一个区间加乘线段树,每次给区间整体乘上$\frac{r-l}{r-l+1}$,然后再加上$\frac{x}{r-l+1}$

查看代码
 struct Segtree {
#define ls (x << 1)
#define rs (x << 1 | 1)
    struct Tag {
        ll k = 1;
        ll b = 0;
    };
    struct Info {
        int sz;
        ll sum = 0;
    };
    struct node {
        Info info;
        Tag tag;
    };
    Info friend operator +(const Info& l, const Info& r) {
        return { l.sz + r.sz,l.sum + r.sum };
    }
    Info friend operator +(const Info& info, const Tag& tag) {
        return { info.sz,(tag.k * info.sum + tag.b * info.sz) % MOD };
    }
    Tag friend operator+(const Tag& tag1, const Tag& tag2) {
        return { tag1.k * tag2.k % MOD,(tag1.b * tag2.k + tag2.b) % MOD };
    }
    int n;
    vector<node> tr;
    Segtree(const vector<int>& a, int n) :n(n) {
        tr.resize(n << 2);
        build(a, 1, 1, n);
    }
    void build(const vector<int>& a, int x, int l, int r) {
        tr[x].info.sz = r - l + 1;
        if (l == r) {
            tr[x].info = { 1,a[l] };
            tr[x].tag = { 1,0 };
            return;
        }
        else {
            int mid = l + r >> 1;
            build(a, ls, l, mid);
            build(a, rs, mid + 1, r);
            pushup(x);
        }
    }
    void pushup(int x) {//从下往上更新
        tr[x].info = tr[ls].info + tr[rs].info;
    }
    void settag(int x, Tag tag) {
        tr[x].info = tr[x].info + tag;
        tr[x].tag = tr[x].tag + tag;
    }
    void pushdown(int x) {//下传标记
        settag(ls, tr[x].tag);
        settag(rs, tr[x].tag);

        tr[x].tag.k = 1;
        tr[x].tag.b = 0;
    }
    //l,r代表操作区间
    void update(int x, int l, int r, int ql, int qr, Tag tag) {
        if (l == ql && r == qr) {
            settag(x, tag);
            return;
        }
        int mid = l + r >> 1;
        pushdown(x);
        if (qr <= mid) update(ls, l, mid, ql, qr, tag);
        else if (mid < ql) update(rs, mid + 1, r, ql, qr, tag);
        else {
            update(ls, l, mid, ql, mid, tag);
            update(rs, mid + 1, r, mid + 1, qr, tag);
        }
        pushup(x);
    }
    Info query(int x, int l, int r, int ql, int qr) {
        if (l == ql && r == qr) return tr[x].info;
        int mid = l + r >> 1;
        pushdown(x);
        if (qr <= mid) return query(ls, l, mid, ql, qr);
        else if (mid < ql) return query(rs, mid + 1, r, ql, qr);
        else return query(ls, l, mid, ql, mid) + query(rs, mid + 1, r, mid + 1, qr);
    }

};

void Solve(int TIME) {

    int n, m;cin >> n >> m;
    vi a(n + 1);for (int i = 1;i <= n;i++) cin >> a[i];
    Segtree tr(a, n);
    for (int i = 1;i <= m;i++) {
        int l, r, x;cin >> l >> r >> x;
        int len = r - l + 1;
        tr.update(1, 1, n, l, r, { (MOD + 1 - inv(len)) % MOD, inv(len) * x % MOD });
    }

    for (int i = 1;i <= n;i++) {
        cout << tr.query(1, 1, n, i, i).sum << " \n"[i == n];
    }

}

 

6.CF103637A - Agile permutation

知识点

置换环,斯特林排列数

题解

肯定是先一直洗牌,直到某个大于等于决策点不再洗牌,然后一直使用交换操作。

枚举这个决策点。决策点即需要交换的次数,在这里即为置换环的个数。

考虑置换环的个数,显然能用斯特林排列数表示。用$SA_{n,k}$表示$1\sim n$的排列中,环的个数为$k$的方案数。

每次同时计算出大于等于决策点的总值和次数,两者相除就是交换操作的期望代价。同时由于是几何分布,期望等于概率的倒数,计算出随机操作的期望代价。两者相加就是该决策点是期望。对所有决策点取最小值即为答案。

还有注意不随机也要计算一下答案,dfs得到置换环数量之后计入即可。

查看代码
 void Solve(int TIME) {

    int n, a, b;cin >> n >> a >> b;
    vi p(n + 1);for (int i = 1;i <= n;i++) cin >> p[i];
    vc<ld> fact(n + 1);fact[0] = 1.0;
    for (int i = 1;i <= n;i++) {
        fact[i] = fact[i - 1] * i;
    }
    vvc<int> SA(n + 1, vi(n + 1));
    SA[0][0] = 1;
    for (int i = 1;i <= n;i++)
        for (int j = 1;j <= i;j++)
            SA[i][j] = SA[i - 1][j - 1] + (i - 1) * SA[i - 1][j];

    vi vis(n + 1);
    auto dfs = [&](auto&& dfs, int u) {
        if (vis[u]) return;
        vis[u] = 1;
        dfs(dfs, p[u]);
        };
    int tot = 0;
    for (int i = 1;i <= n;i++) {
        if (!vis[i]) dfs(dfs, i), tot++;
    }
    ld res = (n - tot) * a;
    ld cnt = 0, val = 0;
    for (int i = n;i >= 1;i--) {
        cnt += SA[n][i], val += 1.0 * SA[n][i] * a * (n - i);
        res = min(res, fact[n] / cnt * b + val / cnt);
    }
    cout << res << endl;

}

 

7.机器人

知识点

无穷级数$\frac{1}{1-x}$

题解

发现要停在一个地方,首先需要先到这个地方,然后转任意圈(可能为0)之后停在这个地方。预处理出转一圈的概率$P$。

设到达某地的概率为$q$,那么停在这个地方的概率是$q+qP+qP^2+...=\frac{q}{1-P}$。

排名的话,手玩一下发现排名依次降低(除了0),额外把为0的项放到最后。

查看代码
 void Solve(int TIME) {

    int n;cin >> n;
    vi k(n + 1);for (int i = 1;i <= n;i++) cin >> k[i];
    int inv2 = inv(2);
    vi INV(5e6 + 1);INV[0] = 1;
    for (int i = 1;i <= 5e6;i++) INV[i] = INV[i - 1] * inv2 % MOD;
    vi p(n + 1);for (int i = 1;i <= n;i++) p[i] = INV[k[i]];
    int pi = 1;
    for (int i = 1;i <= n;i++) {
        pi = pi * p[i] % MOD;
    }
    vi f(n + 1);
    int now = 0;
    int nnow = 0;
    for (int i = 1;i <= n;i++) {
        if (k[i]) f[i] = ++now;
        else f[i] = ++nnow;
    }
    for (int i = 1;i <= n;i++) if (k[i] == 0) f[i] = f[i] + now;
    int t = 1;
    int res = 0, res2 = 0;
    for (int i = 1;i <= n;i++) {
        res = (res + i * t % MOD * (MOD + 1 - p[i]) % MOD * inv(MOD + 1 - pi) % MOD) % MOD;
        res2 = (res2 + i * f[i] % MOD) % MOD;
        t = t * p[i] % MOD;
    }
    cout << res << endl;
    cout << res2 << endl;

}

 

8.P9777 Fujisaki 讨厌数学

知识点

矩阵快速幂

题解

令$a_n=x^n+\frac{1}{x^n}$

$(x+\frac{1}{x})a_{n}=x^{n+1}+x^{n-1}+\frac{1}{x^{n-1}}+\frac{1}{x^{n+1}}=a_{n+1}+a_{n-1}$

即$a_n=(x+\frac1x)a_{n-1}-a_{n-2}=ka_{n-1}-a_{n-2}$​

$\left[\begin{array}\ a_1 &a_2 \end{array}\right] \times\left[\begin{array}\ 0&-1\\1&k \end{array}\right]=\left[\begin{array}\ a_2&a_3 \end{array}\right]$

矩阵快速幂加速即可,$a_1=k,a_2=k^2-2$

查看代码
 void Solve(int TIME) {

    int k, n;cin >> MOD >> k >> n;
    if (n == 0) return cout << 2 << endl, void();
    mat<int> b;b(1, 1) = 0;b(1, 2) = MOD - 1, b(2, 1) = 1, b(2, 2) = k;
    b = qp(b, n - 1);
    mat<int> a;a(1, 1) = k, a(1, 2) = Dec(mul(k, k), 2);
    a = a * b;
    cout << a(1, 1) << endl;

}

 

 

9.D. Lonely Mountain Dungeons (codeforces.com)

知识点

凸函数

题解

固定了队伍数量,那么不同小队里同一物种的分配越均匀越好。

假设共$m$只队伍,$n$个人,设每个队的人数$x_1,x_2,...,x_m$,即$\frac12(x_1(n-x_1)+x_2\cdot(n-x_2)+...+x_m(n-x_m))=\frac12(n(x_1+...+x_m)-(x_1^2+...+x_m^2))$最大,即$\frac12(n^2-(x_1^2+...+x_m^2))$,显然平均分配是最好的。

数据范围很弱,随便模拟一下即可。

查看代码
 void Solve(int TIME) {
 
    int n, b, x;cin >> n >> b >> x;
    map<int, int> mp;
    vi c(n + 1);for (int i = 1;i <= n;i++) cin >> c[i], mp[c[i]]++;
    sort(c.begin() + 1, c.end());
    int mx = c[n];
    auto calc = [&](int x, int gr) {
        int avg = x / gr;
        int re = x % gr;
        int c1 = gr - re, c2 = re;
        return (x * x - c1 * avg * avg - c2 * (avg + 1) * (avg + 1)) / 2;
        };
    int res = 0, now = 0;
    for (int i = 1;i <= mx;i++) {
        if (mp.count(i)) {
            now += mp[i] * calc(i, i);
        }
        int tnow = now;
        for (auto it = mp.upper_bound(i);it != mp.end();it++) {
            auto [j, k] = *it;
            tnow += k * calc(j, i);
        }
        res = max(res, tnow * b - (i - 1) * x);
    }
    cout << res << endl;
 
 
}

打个表看出这是个凸函数,三分也可以做,不过不太会证明为什么是凸函数?

查看代码
 void Solve(int TIME) {

    int n, b, x;cin >> n >> b >> x;
    map<int, int> mp;
    vi c(n + 1);for (int i = 1;i <= n;i++) cin >> c[i], mp[c[i]]++;
    sort(c.begin() + 1, c.end());
    int mx = c[n];
    auto calc = [&](int x, int gr) {
        int avg = x / gr;
        int re = x % gr;
        int c1 = gr - re, c2 = re;
        return (x * x - c1 * avg * avg - c2 * (avg + 1) * (avg + 1)) / 2;
        };

    auto check = [&](int k) {
        if (k <= 0) return -inf;
        int res = 0;
        for (auto [i, j] : mp) {
            res += j * calc(i, k);
        }
        return res * b - (k - 1) * x;
        };

    int l = 1, r = mx;
    while (l <= r) {
        int mid = l + r >> 1;
        if (check(mid - 1) < check(mid)) l = mid + 1;
        else r = mid - 1;
    }

    cout << check(l - 1) << endl;

}

 

10.6.集合划分【算法赛】 - 蓝桥云课 (lanqiao.cn)

知识点

容斥

题解

每个相同位上有$1$的,不能分到同一个集合。但是这样不好算,考虑它的反面,所有该位有$1$的,都分到同一个集合。然后容斥算一下即可,即考虑哪些位需要满足都分到一个集合。这个用并查集算即可。

查看代码
 void Solve(int TIME) {

    int n;cin >> n;
    DSU d(n + 1);
    vi a(n + 1);for (int i = 1;i <= n;i++) cin >> a[i];
    int u = 0;for (int i = 1;i <= n;i++) u |= a[i];
    auto check = [&](int x) {
        vvc<int> del(15);
        int one = 0;
        for (int i = 0;i < 15;i++) {
            if (x >> i & 1) {
                one += 1;
                for (int j = 1;j <= n;j++) {
                    if (a[j] >> i & 1) {
                        if (del[i].size()) d.merge(del[i].back(), j);
                        del[i].push_back(j);
                    }
                }
            }
        }
        int cnt = 0;
        for (int i = 1;i <= n;i++) {
            cnt += d.find(i) == i;
        }
        for (auto i : del) for (auto j : i) d.p[j] = j;
        if (one & 1) return MOD - qp(2, cnt);
        else return qp(2, cnt);
        };

    for (int i = 0;i < 15;i++) {
        if (u >> i & 1) {
            int cnt = 0;
            for (int j = 1;j <= n;j++) {
                if (a[j] >> i & 1) cnt++;
            }
            if (cnt == 1) {
                return cout << 0 << endl, void();
            }
        }
    }

    int res = 0;
    for (int mask = u;;mask = (mask - 1) & u) {
        res = (res + check(mask)) % MOD;
        if (mask == 0) break;
    }
    cout << res << endl;

}

 

由于本题数据很弱导致复杂度$2^n$的解也能过。

错解代码
void Solve(int TIME) {

    int n;cin >> n;
    vi a(n + 1);for (int i = 1;i <= n;i++) cin >> a[i];
    map<ai2, int> mp;
    mp[{0, 0}] = 1;
    int mask = 0;
    int u = (1 << 15);
    for (int i = 1;i <= n;i++) {
        mask |= a[i];
        map<ai2, int> nmp;
        for (auto [c, k] : mp) {
            auto [x, y] = c;
            (nmp[{x | a[i], y}] += k) %= MOD;
            (nmp[{x, y | a[i]}] += k) %= MOD;
        }
        swap(nmp, mp);
    }
    cout << mp[{mask, mask}] << endl;

}

11.E - 7x7x7 (atcoder.jp)

知识点

容斥,公共部分经典trick

首先对于两个在$x$轴上的线段$(x_1,x_2),(x_3,x_4)$来说,公共部分的范围应该是$max(x_1,x_2)\le min(x_3,x_4)$。推广到二维的矩形和三维的立方体也是一样的道理。

由容斥原理,

$v3=V(a\cap b\cap c)$,$v_2=V(a\cap b)+V(b\cap c)+V(a\cap c)-3v_3,v_1=V_{总}-2v_2-3v_3$

暴力枚举第二个和第三个的位置,复杂度$14^6$,看看是否有位置符合。

查看代码
 void Solve(int TIME) {

    int V1, V2, V3;cin >> V1 >> V2 >> V3;
    int V = 3 * 7 * 7 * 7;

    auto func = [&](int x1, int y1, int z1, int x2, int y2, int z2) {
        int res = 1;
        res *= max(0ll, min(x1, x2) + 7 - max(x1, x2));
        res *= max(0ll, min(y1, y2) + 7 - max(y1, y2));
        res *= max(0ll, min(z1, z2) + 7 - max(z1, z2));
        return res;
        };
    auto func2 = [&](int x1, int y1, int z1, int x2, int y2, int z2, int x3, int y3, int z3) {
        int res = 1;
        res *= max(0ll, min({ x1, x2,x3 }) + 7 - max({ x1, x2,x3 }));
        res *= max(0ll, min({ y1, y2,y3 }) + 7 - max({ y1, y2,y3 }));
        res *= max(0ll, min({ z1, z2,z3 }) + 7 - max({ z1, z2,z3 }));
        return res;
        };

    for (int a1 = -7;a1 <= 7;a1++) {
        for (int b1 = -7;b1 <= 7;b1++) {
            for (int c1 = -7;c1 <= 7;c1++) {
                for (int a2 = -7;a2 <= 7;a2++) {
                    for (int b2 = -7;b2 <= 7;b2++) {
                        for (int c2 = -7;c2 <= 7;c2++) {
                            int v3 = func2(0, 0, 0, a1, b1, c1, a2, b2, c2);
                            int v2 = func(a1, b1, c1, a2, b2, c2) + func(0, 0, 0, a1, b1, c1) + func(0, 0, 0, a2, b2, c2) - 3 * v3;
                            int v1 = V - 2 * v2 - 3 * v3;
                            if (v1 == V1 && v2 == V2 && v3 == V3) {
                                YES;
                                cout << 0 << " " << 0 << " " << 0 << " ";
                                cout << a1 << " " << b1 << " " << c1 << " ";
                                cout << a2 << " " << b2 << " " << c2 << endl;
                                return;
                            }
                        }
                    }
                }
            }
        }
    }
    NO;

}

 

D. Exam in MAC (codeforces.com)

知识点

容斥

题解

对于$a_i$,有$\lfloor\frac{a_i}{2}\rfloor+1$对$(x,y)$,满足$x+y=a_i$。

有$c-a_i+1$对$(x,y)$满足$y-x=a_i$。

之后算一下同时满足两个条件的对数,分奇偶算一下即可。

然后容斥一下。

查看代码
 void Solve(int TIME) {

    int n, c;cin >> n >> c;
    vi a(n + 1);for (int i = 1;i <= n;i++) cin >> a[i];
    int res = (c + 1) * (c + 2) / 2;
    ai2 par = { 0,0 };
    for (int i = 1;i <= n;i++) {
        res -= a[i] / 2 + 1;
        res -= c - a[i] + 1;
        par[a[i] & 1]++;
    }
    res += par[0] * (par[0] + 1) / 2;
    res += par[1] * (par[1] + 1) / 2;
    cout << res << endl;

}

17届浙江省赛F

知识点

线性规划Simplex

题解

原本$x\in[-c,c]$,那么首先给所有的$x$减去一个$c$,使得$x$可以取$[0,2c]$。

接下来直接套板子即可。

$w_1x_1+...+w_mx_m+b_1>0$,$w_1'x_1+...+w_m'x_m+b_2<0$

加个$eps$给$AX\le B$变成$>0$和$<0$即可。

最后的答案记得减去$c$。

查看代码
 const ld eps = 1e-10;
constexpr int MAXN = 405, MAXM = 405;
struct Simplex {
    inline static ld a[MAXN][MAXM], b[MAXN], c[MAXM];//矩阵a,约束条件b,
    inline static ld d[MAXN][MAXM], x[MAXM];
    inline static int ix[MAXN + MAXM];
    Simplex() {}
    ld run(int n, int m) {
        m++;
        for (int i = 0; i < m + 1; i++) d[n][i] = d[n + 1][i] = 0;
        for (int i = 0; i < n + m; i++) ix[i] = i;

        int r = n, s = m - 1;
        for (int i = 0; i < n; ++i) {
            for (int j = 0; j < m - 1; ++j) d[i][j] = -a[i][j];
            d[i][m - 1] = 1;
            d[i][m] = b[i];
            if (d[r][m] > d[i][m]) r = i;
        }
        for (int i = 0;i < m - 1;i++) d[n][i] = c[i];
        d[n + 1][m - 1] = -1;
        for (ld dd; ; ) {
            if (r < n) {
                swap(ix[s], ix[r + m]);
                d[r][s] = 1.0 / d[r][s];
                for (int j = 0;j <= m;j++) if (j != s) d[r][j] *= -d[r][s];
                for (int i = 0;i <= n + 1;i++) {
                    if (i != r) {
                        for (int j = 0;j <= m;j++) {
                            if (j != s) {
                                d[i][j] += d[r][j] * d[i][s];
                            }
                        }
                        d[i][s] *= d[r][s];
                    }
                }
            }
            r = s = -1;
            for (int j = 0;j < m;j++) {
                if (s < 0 || ix[s] > ix[j]) {
                    if (d[n + 1][j] > eps || (d[n + 1][j] > -eps && d[n][j] > eps)) s = j;
                }
            }
            if (s < 0) break;
            for (int i = 0; i < n; ++i) {
                if (d[i][s] < -eps) {
                    if (r < 0 || (dd = d[r][m] / d[r][s] - d[i][m] / d[i][s]) < -eps || (dd < eps && ix[r + m] > ix[i + m])) r = i;
                }
            }
            if (r < 0) return -1;//无解
        }
        if (d[n + 1][m] < -eps) return -1;
        ld ans = 0;
        for (int i = 0;i < m;i++) x[i] = 0;
        for (int i = m; i < n + m; i++) {
            if (ix[i] < m - 1) {
                ans += d[i - m][m] * c[ix[i]];
                x[ix[i]] = d[i - m][m];
            }
        }
        return ans;
    }
};

const int C = 9000;
const ld eps2 = 1e-7;

void Solve(int TIME) {

    int n;cin >> n;
    vvc<int> A(2, vi(n));
    vi B(2);
    for (int i = 0;i < 2;i++) {
        int sum = 0;
        for (int j = 0;j < n;j++) {
            cin >> A[i][j];sum += A[i][j];
        }
        cin >> B[i];
        B[i] -= C * sum;
    }
    Simplex sim;
    for (int cas = 0;cas < 2;cas++) {
        for (int i = 0;i < n;i++) sim.c[i] = 1;
        for (int i = 0;i < n;i++) for (int j = 0;j < n;j++) sim.a[i][j] = 0;
        for (int j = 0;j < n;j++) {
            sim.a[j][j] = 1;sim.b[j] = 2 * C;
        }
        for (int i = 0;i < 2;i++) {
            for (int j = 0;j < n;j++) sim.a[n + i][j] = 0;
            for (int j = 0;j < n;j++) {
                if (cas == i) sim.a[n + i][j] = -A[i][j];
                else sim.a[n + i][j] = A[i][j];
            }
            if (cas == i) sim.b[n + i] = B[i] - eps2;
            else sim.b[n + i] = -B[i] - eps2;
        }
        ld res = sim.run(n + 2, n);
        if (res == -1) continue;
        for (int j = 0;j < n;j++) cout << sim.x[j] - C << " ";cout << endl;
        return;
    }
    cout << "No" << endl;

}

6.最后的加k【算法赛】 - 蓝桥云课 (lanqiao.cn)

知识点

矩阵光速幂

题解

注意到每一位是独立的,可以分别计算。列出$dp$方程,然后用矩阵光速幂预处理同底数同模数的矩阵,之后左乘初始矩阵得到答案。

(初始矩阵可以看作向量,这样常数会更快一点)

查看代码
 template<typename T>
struct mat {
    int m[11][11];
    mat() {
        memset(m, 0, sizeof m);
    }
    mat(int epsilon) {
        memset(m, 0, sizeof m);
        for (int i = 1;i <= 10;i++) m[i][i] = 1;
    }
    T& operator()(int i, int j) { return m[i][j]; }
    T operator()(int i, int j)const { return m[i][j]; }
    int is_I() {
        for (int i = 1;i <= 10;i++) {
            for (int j = 1;j <= 10;j++) {
                if (i == j && m[i][j] != 1) return 0;
                if (i != j && m[i][j] != 0) return 0;
            }
        }
        return 1;
    }
};



//矩阵乘法
template<typename T>
mat<T> operator *(const mat<T>& x, const mat<T>& y) {
    mat<T> t;
    for (int k = 1;k <= 10;k++) {
        for (int i = 1;i <= 10;i++) {
            for (int j = 1;j <= 10;j++) {
                (t.m[i][j] += x.m[i][k] * y.m[k][j] % MOD) %= MOD;
            }
        }
    }
    return t;
}
//向量乘矩阵
template<typename T>
mat<T> operator &(const mat<T>& x, const mat<T>& y) {
    mat<T> t;
    for (int k = 1;k <= 10;k++) {
        for (int i = 1;i <= 1;i++) {
            for (int j = 1;j <= 10;j++) {
                (t.m[i][j] += x.m[i][k] * y.m[k][j] % MOD) %= MOD;
            }
        }
    }
    return t;
}
//矩阵快速幂
mat<int> qp(mat<int> a, int k) {
    mat<int> res(1);
    while (k) {
        if (k & 1) res = res * a;
        a = a * a;
        k >>= 1;
    }
    return res;
}


//光速幂,处理同底数同模数的幂
namespace LP_Mat {
    ll getphi(ll x) {
        ll res = x;
        for (int i = 2; i * i <= x; i++) {
            if (x % i == 0) {
                res -= res / i;
                while (x % i == 0) x /= i;
            }
        }
        if (x > 1) res -= res / x;
        return res;
    }
    mat<int> base1[N], basesqrt[N];
    int Block_len;
    int Phi;
    ll maxn = 1e9 + 7;//模数的最大值
    void init(mat<int> x) {//初始化底数为x
        Phi = getphi(MOD);
        Block_len = sqrt(maxn) + 1;
        base1[0] = mat<int>(1);for (int i = 1;i <= Block_len;i++) base1[i] = base1[i - 1] * x;
        basesqrt[0] = mat<int>(1);for (int i = 1;i <= Block_len;i++) basesqrt[i] = basesqrt[i - 1] * base1[Block_len];
    }
    mat<int> qp(ull x) {
        x %= Phi;
        return basesqrt[x / Block_len] * base1[x % Block_len];
    }
}



void Solve(int TIME) {

    int T, x;cin >> T >> x;
    mat<int> base;
    for (int i = 0;i <= 9;i++) {
        int nx = x + i;
        auto s = to_string(nx);
        for (auto j : s) base(i + 1, j - '0' + 1) += 1;
    }
    LP_Mat::init(base);
    while (T--) {
        int n, k;cin >> n >> k;
        mat<int> m;
        for (auto i : to_string(n)) m(1, i - '0' + 1) += 1;
        m = m & LP_Mat::qp(k);
        int res = 0;
        for (int i = 1;i <= 10;i++) res = (res + m(1, i)) % MOD;
        cout << res << endl;
    }

}

P3746 [六省联考 2017] 组合数问题

知识点

矩阵快速幂

题解

$\sum\limits_{i=0}^{n}\left(\begin{array}\ nk\\ik+r \end{array}\right)=\sum\limits_{i=0}^{in+r}\left(\begin{array}\ nk\\i \end{array}\right)[i\equiv r(mod \; k)]$

即从$nk$个物品中选$i$个物品,且$i\equiv r(mod\;k)$的方案数。

于是考虑dp,$f_{i,j}$表示前$i$个物品,选择$j$个(注意需要在模$k$意义下)的方案数。

那么有$f_{i,j}=f_{i-1,j}+f_{i-1,(j-1)\;mod\; k}$,矩阵快速幂即可。注意特判k=1。

查看代码
void Solve(int TIME) {

    mat<int> ma;
    int n, k, r;cin >> n >> MOD >> k >> r;
    if (k == 1) return cout << qp(2, n) << endl, void();
    n *= k;
    for (int i = 0;i < k;i++) {
        ma(i, i) = ma((i - 1 + k) % k, i) = 1;
        //ma(i, i) = ma(i, (i - 1 + k) % k) = 1;原本写反成这个也能通过!?思考一下数据水了还是为啥?
    }

    mat<int> res;
    res(0, 0) = 1;
    res = res * qp(ma, n);
    cout << res(0, r) << endl;

}

6.组合数【算法赛】 - 蓝桥云课 (lanqiao.cn)

双倍经验

查看代码
 void Solve(int TIME) {

    mat<int> ma;
    int a, b, n;cin >> a >> b >> n;

    if (a == 1) return cout << qp(2, n) << endl, void();

    for (int i = 0;i < a;i++) {
        ma(i, i) = ma((i - 1 + a) % a, i) = 1;
    }
    mat<int> res;
    res(0, 0) = 1;
    res = res * qp(ma, n);
    int ans = res(0, b);
    cout << ans << endl;

}

 

 

 

 

标签:lfloor,frac,limits,数论,sum,rfloor,int,数学,杂项
From: https://www.cnblogs.com/mrsuns/p/18133592

相关文章

  • 照亮数学的七道光芒
    目前总分:100+100+50+0+8+0+50=308A(100pts)\[f^k(x)=\dfrac{x^{2^k}}{2^{2^k-2}}=\left(\dfrac{x}{2}\right)^{2^k-2}\timesx^2\]当\(x=2\)时,\(k\)一定会很小就能\(\geq2^n\)或无解。当\(x=3\)时:\[\dfrac{x^{2^k}}{2^{2^k-2}}\geq2......
  • 初等数论——同余
    前置模运算定义:\(a\%b(a\modb)\),表示\(a\)除以\(b\)的余数。加法:\((a+b)\%p\)。减法:\((a-b+p)\%p\)。加\(p\)是为了防止负数。乘法:\((a\timesb)\%p\)。除法无法直接运算,要用逆元(在下面会讲到)。平方运算:快速幂模运算满足:结合律,交换律,分配律。......
  • 洛谷题单指南-数学基础问题-P1069 [NOIP2009 普及组] 细胞分裂
    原题链接:https://www.luogu.com.cn/problem/P1069题意解读:一个数s代表细胞经过一天分裂的个数,则经过t天后个数为st,要计算经过几天后能整除m1m2,也就是st%m1m2==0,有多个s,要计算天数最少就可以满足条件的。解题思路:直接求st%m1m2显然不可取,会超出整数最大范围,高精度也不是好......
  • 【面试准备】窗口函数学习
    昨天面试,技术问的比较简单,甚至没有问算法。业务的话,应该是我没有过面的主要原因,后续展开分析#技术:唯一难倒我的是一个sql##题目:员工表找出每个部门员工年龄最大的两个员工。在MySQL中,你可以使用窗口函数来查询每个部门年龄最大的两名员工。MySQL8.0及以上版本支持窗口函数。以......
  • 基础组合数学
    byTheBigYellowDuck联赛前恶补知识点...排列数&组合数从\(n\)元集合中选出\(m\)个元素的有序排列数记为\(A_n^m\)。计算公式为\[\displaystyleA_n^m=n(n-1)(n-2)\cdots(n-m+1)=\dfrac{n!}{(n-m)!}\]从\(n\)元集合中选出\(m\)个元素的无序排列数记为\(C_n^m\),......
  • 基础数论
    byTheBigYellowDuck联赛前恶补知识点...欧拉函数欧拉函数\(\varphi(n)\)表示\(1\simn\)中与\(n\)互质的数的个数。欧拉函数是积性函数。特殊地,\(\varphi(1)=1\)。对于质数\(p\)显然有\(\varphi(p)=p-1\)。一些常用的结论:设\(p\)是质数,则\(\varphi(p^k)=p^k......
  • 【模板】数学
    高精度类structbint:Vi{ bint(intn=1){resize(n);} voidclr(){while(size()>1&&!back())pop_back();} friendistream&operator>>(istream&io,bint&a){ strings;io>>s; a.clear();rFor(i,sz(s)-1,0)a.pb......
  • 李彦宏:闭源才有真正商业模式才能聚集算力和人才;史上首位数学和计算机最高奖双得主丨RT
       开发者朋友们大家好: 这里是「RTE开发者日报」,每天和大家一起看新闻、聊八卦。我们的社区编辑团队会整理分享RTE(RealTimeEngagement)领域内「有话题的新闻」、「有态度的观点」、「有意思的数据」、「有思考的文章」、「有看点的会议」,但内容仅代表编......
  • 【数学】组合数学 - 排列组合
    父级页面:【数学】组合数学排列组合可重排列可重组合隔板法盒子可以为空隔板法:x个相同的小球,有y个不同的盒子,每个盒子可以为空,求有多少种方案数?把y个不同的盒子视作y-1个不同的隔板,然后把小球视作不同的,全排列有\(A_{x+y-1}^{x+y-1}\)种,然后除以隔板的全排列(隔板之间没有......
  • 【数学】组合数学 - 卡特兰数
    父级页面:【数学】组合数学卡特兰数记号为\(H_n\)第n个卡特兰数,下面的n就是指这个。\(H_0=1,H_1=1,H_2=2,H_3=5,H_4=14,H_5=42\)卡特兰数最常见的场景是合法的括号序,还有栈进出的方案。他们的特点就是“右括号”、“出栈”的次数不能超过剩余的“左括号”、“入栈”的次......