Live2D

Solution -「LOJ #138」「模板」类欧几里得算法

Description

  Link.

  T 组询问,每次给出 n,a,b,c,k1,k2,求

x=0nxk1ax+bck2mod(109+7)

  T=1000n,a,b,c1090k1+k210

Solution

  类欧模板题的集大成者。

  令 f(n,a,b,c,k1,k2) 表示所求答案,尝试通过分类讨论递归到不同的情况求解。

  1. a=0an+b<c

    f(n,a,b,c,k1,k2)=an+bck2x=0nxk1

    预处理 k+1 次多项式 gk(x)=i=0xik 的各项系数,这个就能直接算出来。

  2. 否则若 ac

    f(n,a,b,c,k1,k2)=x=0nxk1(acx+(amodc)x+bc)k2=i=0k2(k2i)acix=0nxk1+i(amodc)x+bck2i=i=0k2(k2i)acif(n,amodc,b,c,k1+i,k2i)

    递归处理。

  3. 否则若 bc

    类似 2.,最终得到

    f(n,a,b,c,k1,k2)=i=0k2(k2i)bcif(n,a,bmodc,c,k1,k2i)

    递归处理。

  4. 对于其余情况

    考虑把高斯函数丢到求和指标上,注意到

    mk=j=0m1[(j+1)kjk]

    以此替代 ax+bck2,并交换求和顺序,得到

    f(n,a,b,c,k1,k2)=j=0an+bc1[(j+1)k2jk2]x=0nxk1[x>cj+cb1a]=j=0an+bc1[(j+1)k2jk2]x=0nxk1j=0an+bc1[(j+1)k2jk2]x=0cj+cb1axk1

    前半部分直接计算,研究后半部分,发现最后一个求和符号是 gk1(cj+cb1a),枚举次数将其展开:

    =i=0k21(k2i)j=0k1+1([xj]gk1)x=0an+bc1xicx+cb1aj=i=0k21(k2i)j=0k1+1([xj]gk1)f(an+bc1,c,cb1,a,i,j)

    其中 i 枚举 (j+1)k2 的次数;j 枚举 gk1 的次数;x 枚举原来的 j。这样也能递归求解了。

  递归中指标变换形如欧几里得算法,设指标值域为 VK=k1+k2,则复杂度为 O(TK4logV)

Code

  实现时,可以对于 n,a,b,c,一次算出所有可能的 f(n,a,b,c,k1,k2) 的值。

/*~Rainybunny~*/

#include <cstdio>
#include <cassert>
#include <iostream>

#define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i )
#define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i )

typedef long long LL;

const int MOD = 1e9 + 7;
int comb[15][15];

inline void subeq( int& a, const int b ) { ( a -= b ) < 0 && ( a += MOD ); }
inline int sub( int a, const int b ) { return ( a -= b ) < 0 ? a + MOD : a; }
inline int mul( const long long a, const int b ) { return int( a * b % MOD ); }
inline int add( int a, const int b ) { return ( a += b ) < MOD ? a : a - MOD; }
inline void addeq( int& a, const int b ) { ( a += b ) >= MOD && ( a -= MOD ); }
inline int mpow( int a, int b ) {
    int ret = 1;
    for ( ; b; a = mul( a, a ), b >>= 1 ) ret = mul( ret, b & 1 ? a : 1 );
    return ret;
}

struct PowerPoly {
    int n, a[12];
    PowerPoly() {}

    PowerPoly( const int k ): n( k ) {
        static int mat[15][15];
        for ( int i = 0, p = 0; i <= n + 1; ++i ) {
            addeq( p, mpow( i, n ) );
            mat[i][n + 2] = p;
        }
        rep ( i, 0, n + 1 ) {
            for ( int j = 0, p = 1; j <= n + 1; ++j, p = mul( p, i ) ) {
                mat[i][j] = p;
            }
        }

        rep ( i, 0, n + 1 ) {
            int p = -1;
            rep ( j, i, n + 1 ) if ( mat[j][i] ) { p = j; break; }
            assert( ~p );
            if ( p != i ) std::swap( mat[i], mat[p] );
            int iv = mpow( mat[i][i], MOD - 2 );
            rep ( j, i + 1, n + 1 ) {
                int t = mul( iv, mat[j][i] );
                rep ( l, i, n + 2 ) subeq( mat[j][l], mul( mat[i][l], t ) );
            }
        }
        per ( i, n + 1, 0 ) {
            a[i] = mul( mat[i][n + 2], mpow( mat[i][i], MOD - 2 ) );
            rep ( j, 0, i - 1 ) subeq( mat[j][n + 2], mul( a[i], mat[j][i] ) );
        }
    }

    inline int operator () ( LL x ) const {
        int ret = 0; x %= MOD;
        for ( int i = 0, p = 1; i <= n + 1; ++i, p = mul( x, p ) ) {
            addeq( ret, mul( a[i], p ) );
        }
        return ret;
    }
};

const PowerPoly pwr[11]
  = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

struct Atom {
    int res[11][11];
    Atom(): res{} {}
    inline int* operator [] ( const int k ) { return res[k]; }
};

/* *
 * calculate \sum_{x=0}^n x^{k_1} \lfloor \frac{ax+b}{c} \rfloor^{k_2},
 * where k_1+k_2 <= 10.
 * */
inline Atom solve( const LL n, const LL a, const LL b, const LL c ) {
    Atom ret; LL m = ( a * n + b ) / c;
    if ( !a || 1ll * a * n + b < c ) {
        rep ( k1, 0, 10 ) {
            int t = int( m % MOD ), p = 1;
            rep ( k2, 0, 10 - k1 ) {
                ret[k1][k2] = mul( p, pwr[k1]( n ) );
                p = mul( p, t );
            }
        }
        return ret;
    }

    if ( a >= c ) {
        Atom tmp( solve( n, a % c, b, c ) );
        rep ( k1, 0, 10 ) rep ( k2, 0, 10 - k1 ) {
            int t = int( ( a / c ) % MOD ), p = 1;
            rep ( i, 0, k2 ) {
                addeq( ret[k1][k2], mul( comb[k2][i],
                  mul( p, tmp[k1 + i][k2 - i] ) ) );
                p = mul( p, t );
            }
        }
        return ret;
    }

    if ( b >= c ) {
        Atom tmp( solve( n, a, b % c, c ) );
        rep ( k1, 0, 10 ) rep ( k2, 0, 10 ) {
            int t = int( ( b / c ) % MOD ), p = 1;
            rep ( i, 0, k2 ) {
                addeq( ret[k1][k2], mul( comb[k2][i],
                  mul( p, tmp[k1][k2 - i] ) ) );
                p = mul( p, t );
            }
        }
        return ret;
    }

    Atom tmp( solve( m - 1, c, c - b - 1, a ) );
    rep ( k1, 0, 10 ) {
        int t = int( m % MOD ), p = 1;
        rep ( k2, 0, 10 - k1 ) {
            ret[k1][k2] = mul( p, pwr[k1]( n ) );
            rep ( i, 0, k2 - 1 ) rep ( j, 0, k1 + 1 ) {
                subeq( ret[k1][k2], mul( comb[k2][i],
                  mul( pwr[k1].a[j], tmp[i][j] ) ) );
            }
            p = mul( t, p );
        }
    }
    return ret;
}

int main() {
    comb[0][0] = 1;
    rep ( i, 1, 10 ) {
        comb[i][0] = 1;
        rep ( j, 1, i ) comb[i][j] = add( comb[i - 1][j - 1], comb[i - 1][j] );
    }

    int n, a, b, c, k1, k2, T;
    for ( scanf( "%d", &T ); T--; ) {
        scanf( "%d %d %d %d %d %d", &n, &a, &b, &c, &k1, &k2 );
        printf( "%d\n", solve( n, a, b, c )[k1][k2] );
    }
    return 0;
}

posted @   Rainybunny  阅读(122)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示