zoukankan      html  css  js  c++  java
  • 「LOJ 2476」蒜头的奖杯

    题目

    点这里看题目,简要题意见下:

    给定 6 个长度均为 \(n\) 的数列 \(A,B,C,D,E,F,G\),求:

    \[\sum_{i=1}^n\sum_{j=1}^n\sum_{k=1}^nA_iB_jC_kD_{\gcd(i,j)}E_{\gcd(i,k)}F_{\gcd(j,k)} \]

    对于 \(100\%\) 的数据,满足 \(1\le n\le 10^5\),且任意数列中的任意一个数都是 \([0,10^{18}]\) 范围内的整数。

    分析

    变量很多,想法之一就是设置枚举变量的“优先次序”:

    \[\begin{aligned} &\sum_{i=1}^n\sum_{j=1}^n\sum_{k=1}^nA_iB_jC_kD_{\gcd(i,j)}E_{\gcd(i,k)}F_{\gcd(j,k)}\\ =&\sum_{i=1}^nA_i\sum_{j=1}^nB_jD_{\gcd(i,j)}\sum_{k=1}^nC_kE_{\gcd(i,k)}F_{\gcd(j,k)}\\ \end{aligned} \]

    其中的 \(\gcd\) 仍然比较难以处理,我们先将一部分用 Mobius 反演展开。设 \(E'=E*\mu,F'=F*\mu\)

    \[\newcommand{\lcm}{\operatorname{lcm}} \newcommand{\div}[2]{\lfloor\frac{#1}{#2}\rfloor} \begin{aligned} &\sum_{i=1}^nA_i\sum_{j=1}^nB_jD_{\gcd(i,j)}\sum_{k=1}^nC_kE_{\gcd(i,k)}F_{\gcd(j,k)}\\ =&\sum_{i=1}^nA_i\sum_{j=1}^nB_jD_{\gcd(i,j)}\sum_{k=1}^nC_k\sum_{p|i,p|k}E'_p\sum_{q|j,q|k}F'_q\\ =&\sum_{p=1}^n\sum_{q=1}^nE'_pF'_q\sum_{p|k,q|k,k\le n}C_k\sum_{p|i,i\le n}\sum_{q|j,j\le n}A_iB_jD_{\gcd(i,j)}\\ =&\sum_{\lcm(p,q)\le n}E'_pF'_q\sum_{\lcm(p,q)|k,k\le n}C_k\sum_{i=1}^{\div{n}{p}}\sum_{j=1}^{\div{n}{q}}A_{ip}B_{jq}D_{\gcd(ip,jq)} \end{aligned} \]

    注意到满足 \(\lcm(p,q)\le n\)\((p,q)\) 对是可以枚举的(?),我们修改式子的形式:

    \[\begin{aligned} &\sum_{\lcm(p,q)\le n}E'_pF'_q\sum_{\lcm(p,q)|k,k\le n}C_k\sum_{i=1}^{\div{n}{p}}\sum_{j=1}^{\div{n}{q}}A_{ip}B_{jq}D_{\gcd(ip,jq)}\\ =&\sum_{d=1}^n\sum_{p=1}^{\div n d}\sum_{q=1}^{\div n {pd}}[\gcd(p,q)=1]E'_{pd}F'_{qd}\sum_{dpq|k,k\le n}C_k\sum_{i=1}^{\div{n}{pd}}\sum_{j=1}^{\div{n}{qd}}A_{ipd}B_{jqd}D_{d\gcd(ip,jq)} \end{aligned} \]

    这样变换过后,所需的下标相应地 scale 了,因此我们调整一下。

    \(m=\div n d\),且对于 \(X=A,B,C,D,E,F\),令 \(x_k=X_{kd}\)

    \[\begin{aligned} &\sum_{d=1}^n\sum_{p=1}^{\div n d}\sum_{q=1}^{\div n {pd}}[\gcd(p,q)=1]E'_{pd}F'_{qd}\sum_{dpq|k,k\le n}C_k\sum_{i=1}^{\div{n}{pd}}\sum_{j=1}^{\div{n}{qd}}A_{ipd}B_{jqd}D_{d\gcd(ip,jq)}\\ =&\sum_{d=1}^n\sum_{p=1}^{m}\sum_{q=1}^{\div{m}{p}}[\gcd(p,q)=1]e'_{p}f'_{q}\sum_{pq|k,k\le m}c_k \sum_{i=1}^{\div{m}{p}}\sum_{j=1}^{\div{m}{q}}a_{ip}b_{jq}d_{\gcd(ip,jq)}\end{aligned} \]

    考虑复杂性的问题——理论上 \(d,p,q\) 都是可以直接枚举的。枚举完 \(d\) 之后,我们有充裕的时间去处理出 \(a,b,c,d,e',f'\),相应地也就可以计算出 \(c^*_r=\sum_{r|k,k\le m}c_k\)。因此,主要的问题在于如何快速地处理出关于 \(i,j\) 的和式。

    我们先设计比较明确的 \(p,q\) 部分:我们可以按 \(\min\{p,q\}\) 的顺序枚举 \((p,q)\) 对,这样就实际上只需要枚举 \(O(\sqrt m)\)\(\min\{p,q\}\)。如果我们枚举好了 \(d\)\(\min\{p,q\}\),则这部分的时间仅有 \(O(\sum_k \sqrt{nk^{-1}})<O(n)\)

    我们尝试在知道了 \(d\)\(\min\{p,q\}\) 的情况下尽快计算出后半部分。不妨将它看作是关于 \(p,q\) 的二元函数 \(g(p,q)=\sum_{i=1}^{\div m p}\sum_{j=1}^{\div m q}a_{ip}b_{jq}d_{\gcd(ip,jq)}\),那么我们相当于知道了其中一个变量的取值,所以只需要处理好 \(g(\min\{p,q\},x)\)\(g(x,\min\{p,q\})\)

    不妨来处理 \(g(\min\{p,q\},x)\) 的情况。仍然是先将 \(d\) 用 Mobius 反演改造一下,设 \(d'=d*\mu\),那么我们将可以得到:

    \[\begin{aligned} &\sum_{i=1}^{\div{m}{p}}\sum_{j=1}^{\div{m}{q}}a_{ip}b_{jq}d_{\gcd(ip,jq)}\\ =&\sum_{i=1}^{\div m p}\sum_{j=1}^{\div m q}a_{ip}b_{jq}\sum_{r|ip,r|jq}d'_{r}\\ =&\sum_{p|i,i\le m}\sum_{q|j,j\le m}\sum_{r|i,r|j}a_ib_jd'_r\\ =&\sum_{q|j}b_j\sum_{r|j}d'_r\sum_{r|i}a_i[p|i] \end{aligned} \]

    唯一的变量 \(q\) 被转移到了最外层和式的枚举条件里,这样我们就可以从内到外,用 Dirichlet 卷积计算出每个 \(q\) 对应的值。对于 \(g(x,\min\{p,q\})\) 的处理同理。

    再研究一下现在的复杂度,为 \(O(\sum_{d}((\frac{n}{d})^{\frac 3 2}\log\log n+\sum_p\sum_q[pq\le \frac n d]))=O(n^{\frac 3 2}\log\log n)\),而且实现起来貌似常数还比较小。

    小结:

    1. 学习一下如何入手一个比较复杂的式子,比如定主元,枚举之类的;
    2. 在化式子的同时,关注计算复杂性。这一部分在某些情况下可以指导化式子的方向。
    3. 关注变量与已知量,调整结构将变量放在靠外的位置

    代码

    #include <cstdio>
    
    #define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
    #define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )
    
    typedef unsigned long long ull;
    
    const int MAXN = 1e5 + 5, MAXS = 330;
    
    template<typename _T>
    void read( _T &x ) {
        x = 0; char s = getchar(); bool f = false;
        while( s < '0' || '9' < s ) { f = s == '-', s = getchar(); }
        while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
        if( f ) x = -x;
    }
    
    template<typename _T>
    void write( _T x ) {
        if( x < 0 ) putchar( '-' ), x = -x;
        if( 9 < x ) write( x / 10 );
        putchar( x % 10 + '0' );
    }
    
    bool cop[MAXN][MAXS], vis[MAXN][MAXS];
    
    ull mu[MAXN];
    int prime[MAXN], pn;
    bool isPrime[MAXN];
    
    ull A[MAXN], B[MAXN], C[MAXN], D[MAXN], E[MAXN], F[MAXN];
    ull A_[MAXN], B_[MAXN], C_[MAXN], D_[MAXN], E_[MAXN], F_[MAXN], tp[MAXN], tq[MAXN];
    
    int N;
    
    bool Chk( const int x, const int y ) {
        if( x < y ) return Chk( y, x );
        if( vis[x][y] ) return cop[x][y];
        vis[x][y] = true;
        if( ! y ) return cop[x][y] = x == 1;
        return cop[x][y] = Chk( y, x % y );
    }
    
    void EulerSieve( const int n ) {
        mu[1] = 1;
        for( int i = 2 ; i <= n ; i ++ ) {
            if( ! isPrime[i] ) 
                mu[prime[++ pn] = i] = -1;
            for( int j = 1 ; j <= pn && 1ll * i * prime[j] <= n ; j ++ ) {
                isPrime[i * prime[j]] = true;
                if( ! ( i % prime[j] ) ) break;
                mu[i * prime[j]] = - mu[i];
            }
        }
    }
    
    void Transform( ull *f, const int n ) {
        static ull tmp[MAXN];
        rep( i, 1, n ) tmp[i] = 0;
        for( int p = 1 ; p <= n ; p ++ )
            for( int q = p ; q <= n ; q += p )
                tmp[q] += f[p] * mu[q / p];
        rep( i, 1, n ) f[i] = tmp[i];
    }
    
    int main() {
        read( N ), EulerSieve( N );
        rep( i, 1, N ) read( A[i] );
        rep( i, 1, N ) read( B[i] );
        rep( i, 1, N ) read( C[i] );
        rep( i, 1, N ) read( D[i] );
        rep( i, 1, N ) read( E[i] );
        rep( i, 1, N ) read( F[i] );
        Transform( E, N );
        Transform( F, N );
        for( int i = 1 ; i <= pn ; i ++ )
            for( int j = N / prime[i] ; j ; j -- )
                C[j] += C[j * prime[i]];
        ull ans = 0;
        rep( d, 1, N ) {
            const int M = N / d;
            rep( i, 1, M )
                A_[i] = A[i * d], D_[i] = D[i * d], 
                B_[i] = B[i * d], E_[i] = E[i * d],
                C_[i] = C[i * d], F_[i] = F[i * d];
            Transform( D_, M );
            for( int p = 1 ; 1ll * p * p <= M ; p ++ ) {
                rep( i, 1, M ) 
                    tq[i] = ( i % p == 0 ) * A_[i],
                    tp[i] = ( i % p == 0 ) * B_[i];
                for( int i = 1 ; i <= pn && prime[i] <= M ; i ++ )
                    for( int j = M / prime[i] ; j ; j -- )
                        tq[j] += tq[j * prime[i]],
                        tp[j] += tp[j * prime[i]];
                rep( r, 1, M ) tq[r] *= D_[r], tp[r] *= D_[r];
                for( int i = 1 ; i <= pn && prime[i] <= M ; i ++ )
                    for( int j = 1 ; j * prime[i] <= M ; j ++ )
                        tq[j * prime[i]] += tq[j],
                        tp[j * prime[i]] += tp[j];
                rep( j, 1, M ) tq[j] *= B_[j], tp[j] *= A_[j];
                for( int i = 1 ; i <= pn && prime[i] <= M ; i ++ )
                    for( int j = M / prime[i] ; j ; j -- )
                        tq[j] += tq[j * prime[i]],
                        tp[j] += tp[j * prime[i]];
                for( int q = p ; 1ll * q * p <= M ; q ++ )
                    if( Chk( p, q ) ) {
                        ans += E_[p] * F_[q] * C_[p * q] * tq[q];
                        if( p ^ q ) ans += E_[q] * F_[p] * C_[p * q] * tp[q];
                    }
            }
        }
        write( ans ), putchar( '\n' );
        return 0;
    }
    
  • 相关阅读:
    hdu 5726 GCD
    codeforces 982C Cut 'em all!
    codeforces 982B Bus of Characters
    codeforces 982A Row
    codeforces 983B XOR-pyramid
    codeforces 979D Kuro and GCD and XOR and SUM
    codeforces 983A Finite or not?
    codeforces 984B Minesweeper
    codeforces 979C Kuro and Walking Route
    codeforces 979B Treasure Hunt
  • 原文地址:https://www.cnblogs.com/crashed/p/15764316.html
Copyright © 2011-2022 走看看