「ARC126F」Affine Sort

题目

点这里看题目。

分析

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

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

\[\lim_{n\rightarrow \infty} \frac{f_n}{n^3}=\lim_{n\rightarrow \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\rightarrow \infty\) 的时候,应该可以\(3n^2\) 去替换 \(b_n-b_{n-1}\)

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

因此我们只需要求:

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


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

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

figure of D

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

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


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

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

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

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


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

合法的 \(\alpha\) 就是“保持大致有序”的 \(\alpha\)。而如何刻画这样的相对关系?我们可以使用距离之和。设 \(f_k(\alpha)\)\(\flo{\alpha X_k}\)\(\flo{\alpha X_{k\bmod 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}^r\flo{\alpha(X_1-X_N)}\mathrm d\alpha\) 即可。而断点显然就是以 \(k\) 为分母的那些分数的位置,由于实际问题中 \(k\) 是整数,这些分数就很好找了。

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

小结:

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

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

    \[\frac{1}{c}(a\bmod b)=\frac{a}{c}\bmod \frac{b}{c} \]

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

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

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

代码

#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( '\n' );
    return 0;
}
posted @ 2021-09-25 14:43  crashed  阅读(100)  评论(0编辑  收藏  举报