zoukankan      html  css  js  c++  java
  • 「ARC126F」Affine Sort

    题目

    点这里看题目。

    分析

    我们可以一眼看出,(f(K)) 本质上就是一个数列,因此我们记 (f_k=f(k),kin mathbb N_+)

    下面是令人震撼的步骤......使用 Stolz 定理,我们可以修改所求极限的形式:

    [lim_{n ightarrow infty} frac{f_n}{n^3}=lim_{n ightarrow infty}frac{f_{n}-f_{n-1}}{3n^2} ]

    为什么是对的?我也说不清楚。

    这里使用到的 Stolz 定理只要求分母上的 (b_n) 严格单增且发散于 (+infty)(b_n=n^3) 显然是一个符合条件的数列。

    另外,(b_n-b_{n-1}=3n^2-3n+1=O(3n^2))。所以当 (n ightarrow infty) 的时候,应该可以(3n^2) 去替换 (b_n-b_{n-1})

    关注这样一个差分的形式,我们可以设 (g_n=f_n-f_{n-1})。根据题意容易得到 (g_n) 的组合含义。

    因此我们只需要求:

    [frac 1 3lim_{n ightarrow infty}frac{g_n}{n^2} ]


    另一个比较巧妙的转化,由于我们只关心相对关系,因此计算 (g_n) 的时候,我们可以考虑 (frac{1}{n}((aX+b)mod n)=(frac{a}{n}X+frac{b}{n})mod 1)。这样的话,我们仍然只需要保证 ((frac{a}{n}X+frac{b}{n})mod 1) 是单调的。为了方便,下面就设 ( ewcommandflo[1]{left{#1 ight}}flo x=xmod 1)

    (alpha=frac a n,eta = frac b n)。当 (n ightarrow infty) 的时候,我们可以取出 (D={(alpha,eta)|alpha,etain [0,1),flo{alpha X_1+eta}<flo{alpha X_2+eta}<dots<flo{alpha X_N+eta}})。这样一来,我们所要求的其实就是 (D) 的面积:

    figure of D

    从有理数是怎么过渡到实数的?

    我们应该是在隐式地做积分。对于任意的 (n),每个合法的 ((a,b)) 其实对应的是一个面积为 (n^{-2}) 的小矩形,求和之后才会得到面积。


    重新回来考虑 ((alpha,eta)) 这个东西。由于我们总在考虑 (mod 1) 的结果,因此我们可以将 ([0,1)) 看成一个环:

    首先,画出一条线段表示 ([0,1));而后,将 0 和 1 粘在一起,这样就得到了需要的环。

    那么对于 (flo{alpha X_k+eta},k=1,2,dots,N)(eta) 的作用其实是保持间距不变地平移环上的 (N) 个点。假如对于某个 (alpha),这 (N) 个点从某个位置开始逆时针看过去是有序的,那么我们就可以用 (eta) 来将 0 移动到那个位置,从而令它们真正有序。

    因此这种情况下,我们只需要让 0 落在 (flo {alpha X_N})(flo{alpha X_1}) 这个区间上即可,因此此时 (eta) 的贡献是 (flo{alpha(X_1-X_N)})


    我们可以敏锐地觉察到,对于所有合法的 (alpha) 来求积分即可得到最终的面积。

    合法的 (alpha) 就是“保持大致有序”的 (alpha)。而如何刻画这样的相对关系?我们可以使用距离之和。设 (f_k(alpha))(flo{alpha X_k})(flo{alpha X_{kmod N+1}}) 的距离,那么如果这 (N) 个值可以保持大致有序,当且仅当有 (sum_{k=1}^{N}f_k(alpha)=1)

    现在我们来研究单个函数 (h=flo{ka})。下图展示了 (k<0)(k>0) 的图像:

    figure of h

    可以发现,(h) 的图像其实就是在某些点有 (pm 1) 的跳变(跳变的位置就是 (h(alpha)=0 or 1) 的位置)的斜率为 (k) 的一次函数图像。而 (sum_{k=1}^Nf_k(alpha)) 的斜率一定是 0,所以最终它的图像看起来像若干段水平线段拼起来。

    我们只需要找出所有的断点和对应的变化值就可以给出 (sum_{k=1}^N f_k) 的图像,找出函数值为 1 的那些段求定积分 (int_{l}^rflo{alpha(X_1-X_N)}mathrm dalpha) 即可。而断点显然就是以 (k) 为分母的那些分数的位置,由于实际问题中 (k) 是整数,这些分数就很好找了。

    总共断点数为 (O(sum X)),复杂度完全可以接受。

    小结:

    1. 数学定理......不说了,不会也没有办法;

    2. 最巧妙的转化出现在,将余数转化为小数部分这一步骤。这个东西可以推广:

      [frac{1}{c}(amod b)=frac{a}{c}mod frac{b}{c} ]

      不知道这个有啥用,反正如果不需要具体地考虑余数值,而只关心相对大小,都可以尝试这个转化。

    3. 另外两个地方值得注意:

      • 对于 (eta) 的处理,通过“环”的模型我们可以直接计算 (eta) 的贡献;
      • 对于有序性的处理,我们通过“距离”来刻画它。其实这应该是一个挺通用的想法,尤其是在环上。

    代码

    #include <cstdio>
    #include <algorithm>
    
    #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 long long LL;
    
    const int mod = 998244353, inv2 = ( mod + 1 ) / 2;
    const int MAXS = 1e6 + 5;
    
    template <typename _T>
    void read( _T &x )
    {
        x = 0; char s = getchar(); bool f = false;
        while( ! ( '0' <= s && s <= '9' ) ) { 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' );
    }
    
    inline int Mul( int, int );
    inline int Inv( const int );
    inline LL Gcd( LL x, LL y ) { for( LL z ; y ; z = x, x = y, y = z % y ); return x; }
    
    struct Fraction
    {
        LL fz, fm;
    
        Fraction(): fz( 0 ), fm( 1 ) {}
        Fraction( LL Z ): fz( Z ), fm( 1 ) {}
    
        Fraction& operator *= ( const Fraction &g ) { return *this = *this * g; }
    
        Fraction( LL Z, LL M )
        {
            LL d = Gcd( Z, M );
            if( d ) fz = Z / d, fm = M / d;
        }
    
        Fraction operator * ( const Fraction &g ) const
        {
            LL d1 = Gcd( fz, g.fm ), d2 = Gcd( fm, g.fz );
            return Fraction( fz / d1 * g.fz / d2, fm / d2 * g.fm / d1 );
        }
    
        Fraction operator + ( const Fraction &g ) const
        {
            LL d = Gcd( fm, g.fm );
            return Fraction( g.fm / d * fz + fm / d * g.fz, fm / d * g.fm );
        }
    
        Fraction operator - ( const Fraction &g ) const
        {
            LL d = Gcd( fm, g.fm );
            return Fraction( g.fm / d * fz - fm / d * g.fz, fm / d * g.fm );
        }
    
        inline LL Floor() const { return fz / fm; }
        inline LL Ceil() const { return ( fz + fm - 1 ) / fm; }
        inline operator int() const { return Mul( fz % mod, Inv( fm % mod ) ); }
        inline bool operator < ( const Fraction &g ) const { return fz * g.fm < fm * g.fz; }
        inline bool operator == ( const Fraction &g ) const { return fz == g.fz && fm == g.fm; }
    };
    
    typedef std :: pair<Fraction, int> Point;
    
    Point brk[MAXS];
    
    int X[MAXS];
    int N, tot = 0;
    
    inline int Qkpow( int, int );
    inline int Mul( int x, int v ) { return 1ll * x * v % mod; }
    inline int Sub( int x, int v ) { return ( x -= v ) < 0 ? x + mod : x; }
    inline int Add( int x, int v ) { return ( x += v ) >= mod ? x - mod : x; }
    inline int Inv( const int a ) { return Qkpow( a, mod - 2 ); }
    inline int Sqr( const int x ) { return Mul( x, x ); }
    
    inline int Qkpow( int base, int indx )
    {
        int ret = 1;
        while( indx )
        {
            if( indx & 1 ) ret = Mul( ret, base );
            base = Mul( base, base ), indx >>= 1;
        }
        return ret;
    }
    
    int Integral( Fraction lim )
    {
        int ret = 0;
        if( X[1] > X[N] )
        {
            LL tmp = ( lim * Fraction( X[1] - X[N] ) ).Floor();
            ret = Mul( tmp, Mul( Inv( Sub( X[1], X[N] ) ), inv2 ) );
            int len = lim - Fraction( tmp, X[1] - X[N] );
            ret = Add( ret, Mul( Mul( inv2, Sub( X[1], X[N] ) ), Sqr( len ) ) );
        }
        else
        {
            LL tmp = ( lim * Fraction( X[N] - X[1] ) ).Ceil();
            ret = Mul( tmp, Mul( Inv( Sub( X[N], X[1] ) ), inv2 ) );
            int len = Fraction( tmp, X[N] - X[1] ) - lim;
            ret = Sub( ret, Mul( Mul( inv2, Sub( X[N], X[1] ) ), Sqr( len ) ) );
        }
        return ret;
    }
    
    int main()
    {
        read( N );
        rep( i, 1, N ) read( X[i] );
        X[N + 1] = X[1];
        rep( i, 1, N )
            if( X[i] < X[i + 1] )
                rep( j, 1, X[i + 1] - X[i] )
                brk[++ tot] = Point( Fraction( j, X[i + 1] - X[i] ), - 1 );
        else
            rep( j, 0, X[i] - X[i + 1] - 1 )
            brk[++ tot] = Point( Fraction( j, X[i] - X[i + 1] ), + 1 );
        Fraction nxt;
        int y = 0, ans = 0;
        brk[++ tot] = Point( 0, 0 );
        brk[++ tot] = Point( 1, 0 );
        std :: sort( brk + 1, brk + 1 + tot );
        for( int i = 1, j ; i <= tot ; i = j )
        {
            for( j = i ; j <= tot && brk[i].first == brk[j].first ; j ++ );
            for( int k = i ; k < j ; k ++ ) y += brk[k].second;
            if( y == 1 && j < tot ) 
                ans = Add( ans, Sub( Integral( brk[j].first ), Integral( brk[i].first ) ) );
        }
        write( Mul( Inv( 3 ), ans ) ), putchar( '
    ' );
        return 0;
    }
    
  • 相关阅读:
    Java
    Leetcode 计划
    Java虚拟机
    浅谈 MVP in Android
    【PAT】B1074 宇宙无敌加法器(20 分)
    【PAT】B1076 Wifi密码(15 分)
    【PAT】B1075 链表元素分类(25 分)
    【PAT】B1077 互评成绩计算(20 分)
    【PAT】B1078 字符串压缩与解压(20 分)
    【PAT】B1079 延迟的回文数(20 分)
  • 原文地址:https://www.cnblogs.com/crashed/p/15334354.html
Copyright © 2011-2022 走看看