【bzoj4921】[Lydsy六月月赛]互质序列 暴力

题目描述

给出一个序列,要求删除一段非空区间,使得剩下的数的个数大于等于2。求所有删除方式剩下的数的最大公约数的和。

输入

 

第一行包含一个正整数n(3<=n<=100000),表示序列的长度。
第二行包含n个正整数a_1,a_2,...,a_n(1<=a_i<=10^9),分别表示序列中的每个元素。

 

输出

 

输出一行一个整数,即E*S mod 998244353的值。

 

样例输入

5
3 4 5 2 9

样例输出

14


题解

暴力

显然剩下的一定是左边的一段及右边的一段,可以分别枚举左右的位置。

根据 【bzoj4052】[Cerc2013]Magical GCD 的结论,一个数不断地与其它数取gcd,最多只会有log个不同的结果,因为gcd减少一次至少要除以2。

因此可以预处理出从左到右、从右向左的相同gcd段,然后直接枚举左右的段,考虑有多少个区间,直接乱搞即可。

时间复杂度 $O(n\log n)$

#include <cstdio>
#include <cctype>
#define N 100010
#define mod 998244353
typedef long long ll;
int n , a[N] , lp[35] , lv[35] , lt = 1 , rp[35] , rv[35] , rt = 1;
inline char nc()
{
	static char buf[100000] , *p1 , *p2;
	return p1 == p2 && (p2 = (p1 = buf) + fread(buf , 1 , 100000 , stdin) , p1 == p2) ? EOF : *p1 ++ ;
}
inline int read()
{
	int ret = 0; char ch = nc();
	while(!isdigit(ch)) ch = nc();
	while(isdigit(ch)) ret = ((ret + (ret << 2)) << 1) + (ch ^ '0') , ch = nc();
	return ret;
}
int gcd(int a , int b)
{
	return b ? gcd(b , a % b) : a;
}
inline ll calc(int a , int b , int c , int d)
{
	if(d - c < 4) return 0;
	if(b - a >= 2) return 1ll * (a - c) * (d - b);
	if(a > d - 3) a = d - 3;
	if(b < c + 3) b = c + 3;
	return 1ll * (a - c) * (d - b) - 1ll * (a - b + 2) * (a - b + 3) / 2;
}
int main()
{
	int i , j , t;
	long long ans = 0;
	n = read();
	for(i = 1 ; i <= n ; i ++ ) a[i] = read();
	lp[1] = 1 , lv[1] = a[1];
	for(i = 2 ; i <= n ; i ++ )
	{
		if((t = gcd(lv[lt] , a[i])) == lv[lt]) lp[lt] ++ ;
		else lp[++lt] = i , lv[lt] = t;
		if(i < n) ans = (ans + lv[lt]) % mod;
	}
	rp[1] = n , rv[1] = a[n];
	for(i = n - 1 ; i ; i -- )
	{
		if((t = gcd(rv[rt] , a[i])) == rv[rt]) rp[rt] -- ;
		else rp[++rt] = i , rv[rt] = t;
		if(i > 1) ans = (ans + rv[rt]) % mod;
	}
	rp[0] = n + 1;
	for(i = 1 ; i <= lt ; i ++ )
		for(j = 1 ; j <= rt ; j ++ )
			ans += gcd(lv[i] , rv[j]) * calc(lp[i] , rp[j] , lp[i - 1] , rp[j - 1]);
	printf("%lld\n" , ans % mod);
	return 0;
}

 

 

posted @ 2017-12-20 14:51  GXZlegend  阅读(474)  评论(0编辑  收藏  举报