Live2D

Solution -「ARC 126F」Affine Sort

Description

  Link.

  给定 {xn},令

f(k)=|{(a,b,c)a,b[0,c),c[1,k],(i[1,n),(axi+b)modc<(axi+1+b)modc)}|

求出

limk+f(k)k3mod998244353.

  n103x5×105

Solution

  太炫酷了叭。

  令 g(k)=f(k)f(k1),断言这样一条性质:

limk+f(k)k3=limk+g(k)3k2.

甚至可以认为给原式洛了一发。

证明   假定存在 c=limk+g(k)k2,则对于任意 εR+,应有足够大的 k,使得 (cε)k2g(k)(c+ε)k2。继而,也应有足够大的 k 使得 (cε)iki2f(k)(c+ε)iki2。考虑到 iki2=k33+O(k2),可以得到 13cεf(k)k313c+ε。所以基于这个假设,得到 limk+f(k)k3=13limk+g(k)k2

  此后,考虑使得 (axi+b)modk 单增的 (a,b),根据取模的性质,应该有 {axi+bk} 单增,其中 {x}=xx 表示取非负数 x 的小数部分。所以,令 α=ak,β=bk,我们仅需研究使得 {αxi+β} 单增的 (α,β)[0,1)2。具体地,令

D={(α,β)[0,1)2i[1,n),{αxi+β}<{αxi+1+β}},

我们所求即为 D 的面积。

  这步转化的具体数学原理我并没有学懂,这貌似也涉及了上文中“假定极限存在”的证明。当然存在一些感性理解方法也显得不够具有说服力。如果希望深刻研究请参考 官方题解 叭,这里先跳过,之后的步骤仍有思考价值。

  我们把 {yi={αxi+β}} 放在 [0,1) 这个关于 1 的“模域”上(Tiw: 你把“模”和“域”放一起是完全不合理的!我:?),进一步研究 (α,β) 的性质:

  可见,α 能决定 yi 在圆周上的相对位置关系,而 β 仅能整体旋转 y,也即是确定 0 的位置。显然,对于使得相对位置合法的 α,合法 β 取值区间的长度为 {α(x1xn)};另一方面,对于这样的 α,应有

i=1n({αxi+1}{αxi})mod1=1.    (xn+1=x1)

同时,α[0,1)xiN,则 {αxi+1}{αxi}{α(xi+1xi)}(mod1),可见在 α01 取值时,仅有经过某个 t|xi+1xi| 会改变左式的值,即这些点把 [0,1) 分隔为若干区间,若一个区间合法,再积分求此时 β 的贡献,即 lr{α(x1xn)}dα。注意 |xnxn+1| 也在分隔点的分母中,所以这是单纯的一次函数积分。

  最终,复杂度瓶颈为取出 O(x) 个区间排序,即 O(xlogx)

Code

/*~Rainybunny~*/

#include <bits/stdc++.h>

#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 )

const int MAXN = 1e3, MOD = 998244353, INV6 = 166374059;
int n, x[MAXN + 5];

inline int iabs( const int u ) { return u < 0 ? -u : u; }
inline int mul( const int u, const int v ) { return 1ll * u * v % MOD; }
inline int sub( int u, const int v ) { return ( u -= v ) < 0 ? u + MOD : u; }
inline int add( int u, const int v ) { return ( u += v ) < MOD ? u : u - MOD; }
inline int mpow( int u, int v = MOD - 2 ) {
	int ret = 1;
	for ( ; v; u = mul( u, u ), v >>= 1 ) ret = mul( ret, v & 1 ? u : 1 );
	return ret;
}

struct Atom {
	int a, b, c;
	inline bool operator < ( const Atom& t ) const {
		return 1ll * a * t.b < 1ll * t.a * b;
	}
};
std::vector<Atom> sec;

int main() {
	scanf( "%d", &n );
	rep ( i, 1, n ) scanf( "%d", &x[i] );
	x[n + 1] = x[1];
	
	int circ = 0;
	rep ( i, 1, n ) {
		int k = iabs( x[i] - x[i + 1] ), d = x[i] < x[i + 1] ? -1 : 1;
		rep ( j, 1, k - 1 ) sec.push_back( { j, k, d } );
		circ += x[i] >= x[i + 1];
	}
	sec.push_back( { 0, 1, 0 } ), sec.push_back( { 1, 1, 0 } );
	std::sort( sec.begin(), sec.end() );
	
	int ans = 0;
	for ( size_t i = 1; i < sec.size(); ++i ) {
		if ( ( circ += sec[i - 1].c ) != 1 ) continue;
		int dn = sub( mul( sec[i].a, mpow( sec[i].b ) ),
		  mul( sec[i - 1].a, mpow( sec[i - 1].b ) ) );
		
		int le = ( 1ll * sec[i - 1].a * ( x[1] - x[n] ) % sec[i - 1].b
		  + sec[i - 1].b ) % sec[i - 1].b;
		if ( !le && x[1] < x[n] ) le = sec[i - 1].b;
		le = mul( le, mpow( sec[i - 1].b ) );
		
		int ri = ( 1ll * sec[i].a * ( x[1] - x[n] ) % sec[i].b
		  + sec[i].b ) % sec[i].b;
		if ( !ri && x[1] > x[n] ) ri = sec[i].b;
		ri = mul( ri, mpow( sec[i].b ) );
		
		ans = add( ans, mul( add( le, ri ), dn ) );
	}
	printf( "%d\n", mul( ans, INV6 ) );
	return 0;
}

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