【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; }