Be Geeks!

题目:
https://ac.nowcoder.com/acm/contest/7817/B

给你 n n n个数,设 G ( i , j ) = g c d ( a i , a i + 1 . . . a j ) G(i,j)=gcd(a_i,a_{i+1}...a_j) G(i,j)=gcd(ai,ai+1...aj) M ( i , j ) = m a x ( a i , a i + 1 . . . a j ) M(i,j)=max(a_i,a_{i+1}...a_j) M(i,j)=max(ai,ai+1...aj),计算
∑ 1 ≤ i ≤ j ≤ n G ( i , j ) ⋅ M ( i , j ) \sum_{1\le i\le j\le n}G(i,j)\cdot M(i,j) 1ijnG(i,j)M(i,j)

思路:
这里要解决两个问题,一个是区间 g c d gcd gcd,一个是区间最大值求和。

  • 首先对于 g c d gcd gcd来说,可以 n l o g m nlog^m nlogm,预处理出来, m m m a i a_i ai的最大值。假设右端点确定,则左端点越往左 g c d gcd gcd非严格递减,如果减少每次至少除 2 2 2,所以 g c d gcd gcd的个数是 l o g log log级别。所以区间然后可以通过 d p dp dp预处理出来每种 g c d gcd gcd最左边端点。
  • 区间 g c d gcd gcd预处理出来后,枚举右端点 i i i,再枚举每种 g c d gcd gcd的最左端端点 j j j,假设上一次左端点为 k k k,我们要计算的是
    ∑ l = j k − 1 G ( l , i ) ⋅ M ( l , i ) \sum_{l=j}^{k-1}G(l,i)\cdot M(l,i) l=jk1G(l,i)M(l,i)
    G ( l , i ) G(l,i) G(l,i)都是一样的,现在就是要计算最大值的和,可以预处理出一个前缀和,这前缀和是以 i i i为右端点往左边的最长递增序列,而每一个位置上的贡献就是这个位置上的值乘上和左边第一个比他大的值的距离。然后瞎搞计算。
#include<bits/stdc++.h>
#define ll long long
#define mod 1000000007
using namespace std;
const int N=200009;
struct node {
    int l;
    ll val;
    node(int x,ll y):l(x),val(y) {}
};
int n,dp[N];
ll a[N],sum[N],ans=0;
vector<node>w[N];

namespace ST {
int a[N],n,dp[N][25],index[N][25],mm[N];//mm:logn/log2
void ST() {
    mm[0]=-1;
    for(int i=1; i<=n; ++i) {
        dp[i][0]=a[i];
        index[i][0]=i;

        mm[i]=((i&(i-1))==0)?mm[i-1]+1:mm[i-1];
    }
    for(int j=1; j<=mm[n]; ++j)
        for(int i=1; (i+(1<<j)-1)<=n; ++i) {
            dp[i][j]=max(dp[i][j-1],dp[i+(1<<(j-1))][j-1]);
            if(dp[i][j-1]<=dp[i+(1<<(j-1))][j-1]) {
                index[i][j]=index[i+(1<<(j-1))][j-1];
            } else {
                index[i][j]=index[i][j-1];
            }
        }

}
int query(int x,int y) {
    int t=mm[y-x+1];
    //return max(dp[x][t],dp[y-(1<<t)+1][t]);
    if(dp[x][t]<=dp[y-(1<<t)+1][t])
        return index[y-(1<<t)+1][t];
    else return index[x][t];
}
}



int main() {
    scanf("%d",&n);
    ST::n=n;
    for(int i=1; i<=n; i++)
        scanf("%lld",&a[i]),ST::a[i]=a[i];
    ST::ST();
    a[0]=mod;
    for(int i=1; i<=n; i++) {
        int tmp=i-1;
        while(a[tmp]<=a[i])
            tmp=dp[tmp];
        dp[i]=tmp;
    }
    sum[0]=0;
    for(int i=1; i<=n; i++)
        sum[i]=(sum[dp[i]]+(i-dp[i])*a[i]%mod)%mod;
    for(int i=1; i<=n; i++) {
        int si1=1;
        w[i].push_back(node(i,a[i]));
        int si=w[i-1].size();
        for(int j=0; j<si; j++) {
            ll tmp=__gcd(a[i],w[i-1][j].val);
            if(tmp==w[i][si1-1].val)
                w[i][si1-1].l=w[i-1][j].l;
            else
                w[i].push_back(node(w[i-1][j].l,tmp)),si1++;
        }
    }
    /*for(int i=1; i<=n; i++)
        for(int j=0; j<w[i].size(); j++)
            printf("%d %d %lld\n",i,w[i][j].l,w[i][j].val);*/
    for(int i=1; i<=n; i++) {
        int si=w[i].size();
        ll tmp=0;
        for(int j=0; j<si; j++) {
            int locate=ST::query(w[i][j].l,i);
            ll ans1;
            ll Sum=(sum[i]-sum[locate]+mod+a[locate]*(locate-w[i][j].l+1)%mod)%mod;
            ans1=(Sum*w[i][j].val%mod-tmp*w[i][j].val%mod+mod)%mod;
            ans=(ans+ans1)%mod;
            tmp=Sum;
            //printf("%d %d %d %lld %lld\n",i,w[i][j].l,locate,w[i][j].val,ans1);
        }
    }
    printf("%lld",ans);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值