您的位置:首页 > 其它

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

2017-12-20 14:51 507 查看
题目描述

给出一个序列,要求删除一段非空区间,使得剩下的数的个数大于等于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
, 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
;
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: