Live2D

Solution -「COCI 2014-2015 #2」「洛谷 P6406」Norma

Description

  Link.

  给定 {an},求:

i=1nj=in(ji+1)mink=ij{ak}maxk=ij{ak}

  答案对 109 取模。

Solution

  挺可爱的一道题 w。

  静态序列计数问题,可以考虑分治:对于 [l,r]l<r),令分割点 p=l+r2,对满足左端点在 [l,p],右端点在 (p,r] 的区间进行计算,然后递归两个区间继续求解。

  对于本题,可以枚举区间左端点 i=p,p1,,l,记 s=minu=ip{au}t=maxu=ip{au}j=maxu(p,r]{u|minv=ij{av}=s}k=maxu(p,r]{u|maxv=ij{av}=t},只讨论 jk,发现对于将要计数的区间 [i,x],分三种情况:

  • x(p,j]:最小值、最大值都在 [l,p] 中取到,那么只需要关心 x 的位置,故此情况对答案的贡献为:

    pqx(p,j](xi+1)

  • x(j,k]:最大值在 [l,p] 中取到,而最小值会在 (p,x] 中取到,则要求:

    qx(j,k](xi+1)minu=p+1x{au}

    求和内是一个距离 × 权值的形式,尝试预处理出值的前缀和和下标 × 值的前缀和,就能 O(1) 计算了,这里略过,详见代码。

  • x(k,r]:最小值、最大值都在 (p,x] 中取到,类似地求:

    x(k,r](xi+1)minu=p+1x{au}maxu=p+1x{au}

    仍然预处理出两种前缀和,O(1) 计算。

  综上,在分治的 O(nlogn) 的时间内解决本题。

Code

/* Clearink */

#include <cstdio>

inline int rint () {
	int x = 0; char s = getchar ();
	for ( ; s < '0' || '9' < s; s = getchar () );
	for ( ; '0' <= s && s <= '9'; s = getchar () ) x = x * 10 + ( s ^ '0' );
	return x;
}

const int MAXN = 5e5, MOD = 1e9;
int n, a[MAXN + 5], ans;
int smx[MAXN + 5], vmx[MAXN + 5];
int smn[MAXN + 5], vmn[MAXN + 5];
int sxn[MAXN + 5], vxn[MAXN + 5];

inline void chkmin ( int& a, const int b ) { b < a && ( a = b, 0 ); }
inline void chkmax ( int& a, const int b ) { a < b && ( a = b, 0 ); }
inline int mul ( const long long a, const int b ) { return a * b % MOD; }
inline int sub ( int a, const int b ) { return ( a -= b ) < 0 ? a + MOD : a; }
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, 0 ); }
inline int sum ( const int l, const int r ) {
	return r < l ? 0 : ( ( l + r ) * ( r - l + 1ll ) >> 1 ) % MOD;
}

inline void solve ( const int l, const int r ) {
	if ( l == r ) return addeq ( ans, mul ( a[l], a[l] ) );
	int mid = l + r >> 1;
	smx[mid] = vmx[mid] = smn[mid] = vmn[mid] = sxn[mid] = vxn[mid] = 0;
	for ( int i = mid + 1, mn = a[mid + 1], mx = a[mid + 1];
		i <= r; ++i, chkmin ( mn, a[i] ), chkmax ( mx, a[i] ) ) {
		vmx[i] = add ( vmx[i - 1], mx ),
		smx[i] = add ( smx[i - 1], mul ( i - mid, mx ) );
		vmn[i] = add ( vmn[i - 1], mn ),
		smn[i] = add ( smn[i - 1], mul ( i - mid, mn ) );
		vxn[i] = add ( vxn[i - 1], mul ( mn, mx ) ),
		sxn[i] = add ( sxn[i - 1], mul ( i - mid, mul ( mn, mx ) ) );
	}
	for ( int i = mid, mn = a[i], mx = a[i], j = mid + 1, k = mid + 1;
		i >= l; --i, chkmin ( mn, a[i] ), chkmax ( mx, a[i] ) ) {
		for ( ; j <= r && mn <= a[j]; ++j );
		for ( ; k <= r && a[k] <= mx; ++k );
		int p = j < k ? j : k, q = j ^ k ^ p; // [mid+1,p),[p,q),[q,r].
		addeq ( ans, mul ( mul ( mn, mx ), sum ( mid + 2 - i, p - i ) ) );
		if ( j <= k ) { // max is constant while min will change.
			addeq ( ans, mul ( mx, add (
				mul ( mid - i + 1, sub ( vmn[k - 1], vmn[j - 1] ) ),
				sub ( smn[k - 1], smn[j - 1] ) ) ) );
		} else { // min is constant while max will change.
			addeq ( ans, mul ( mn, add (
				mul ( mid - i + 1, sub ( vmx[j - 1], vmx[k - 1] ) ),
				sub ( smx[j - 1], smx[k - 1] ) ) ) );
		}
		addeq ( ans, add (
			mul ( mid - i + 1, sub ( vxn[r], vxn[q - 1] ) ),
			sub ( sxn[r], sxn[q - 1] ) ) );
	}
	solve ( l, mid ), solve ( mid + 1, r );
}

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