【gcd分块】BZOJ4921[互质序列]题解

来源:互联网 发布:网络运营托管 编辑:程序博客网 时间:2024/05/29 14:36

题目概述

给出一个序列 {an} ,现在可以删除一个非空子段,贡献为删除后剩下数(至少两个)的 gcd ,求总贡献值。

解题报告

预处理前缀 gcd 和后缀 gcd ,然后枚举删除子段的左端点,如果再枚举右端点肯定无法承受,而由于后缀 gcd 只有 log2a 块,所以枚举块即可,效率 O(nlog2a)

示例程序

#include<cstdio>#include<algorithm>using namespace std;typedef long long LL;const int maxn=100000,MOD=998244353;int n,blk,a[maxn+5],pre[maxn+5],suf[maxn+5],R[maxn+5],ans;int gcd(int a,int b) {if (!b) return a;return gcd(b,a%b);}inline void AMOD(int &x,int tem) {if ((x+=tem)>=MOD) x-=MOD;}int main(){    freopen("program.in","r",stdin);    freopen("program.out","w",stdout);    scanf("%d",&n);for (int i=1;i<=n;i++) scanf("%d",&a[i]);    pre[1]=a[1];for (int i=2;i<=n;i++) pre[i]=gcd(pre[i-1],a[i]);    suf[n]=a[n];R[++blk]=n;    for (int i=n-1;i>=1;i--) {suf[i]=gcd(suf[i+1],a[i]);if (suf[i]!=suf[i+1]) R[++blk]=i;}    R[++blk]=0;for (int i=2;i<=n-1;i++) AMOD(ans,pre[i]),AMOD(ans,suf[i]);    for (int i=2;i<n;i++)    for (int j=2;j<=blk;j++) if (i<=R[j-1]-1)        AMOD(ans,(LL)gcd(pre[i-1],suf[R[j-1]])*(R[j-1]-max(i,R[j]))%MOD);    return printf("%d\n",ans),0;}