#4702. gcd

题目描述

题解

考虑从大到小枚举 $gcd$ ,假设 $gcd$ 的倍数出现的位置从小到大为 $p_1,p_2,...,p_{t-1},p_t$ ,那它影响的区间为 $[1,p_{t-1}-1],[p_1+1,p_t-1],[p_2+1,n]$

考虑对每个点 $l$ 维护一个右端点 $r$ 表示 $[l,u](l\le u \le r)$ 都被影响过了,每次计算答案的时候考虑用总区间数减去被影响过区间数,发现随着 $l$ 的增大 $r$ 是单调递增的,于是线段树上二分即可

代码

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=2e5+5;
int T,n,t,a[N],b[N],m,h[N<<2],tg[N<<2],g[N];
LL s,f[N<<2]; struct O{int k,l,r;}S[25];
#define Ls k<<1
#define Rs k<<1|1
#define mid ((l+r)>>1)
void up(int k){
    f[k]=f[Ls]+f[Rs];
    h[k]=min(h[Ls],h[Rs]);
}
void push(int k,int l,int r,int v){
    h[k]=tg[k]=v;f[k]=1ll*(r-l+1)*v;
}
void down(int k,int l,int r){
    push(Ls,l,mid,tg[k]);
    push(Rs,mid+1,r,tg[k]);
    tg[k]=0;
}
void build(int k,int l,int r){
    tg[k]=0;if (l==r){f[k]=h[k]=l-1;return;}
    build(Ls,l,mid);build(Rs,mid+1,r);up(k);
}
void upd(int k,int l,int r,int L,int R,int v){
    if (L<=l && r<=R) return push(k,l,r,v);
    if (tg[k]) down(k,l,r);
    if (mid>=L) upd(Ls,l,mid,L,R,v);
    if (mid<R) upd(Rs,mid+1,r,L,R,v);
    up(k);
}
void find(int k,int l,int r,int L,int R){
    if (L<=l && r<=R){
        S[++t]=(O){k,l,r};return;
    }
    if (tg[k]) down(k,l,r);
    if (mid>=L) find(Ls,l,mid,L,R);
    if (mid<R) find(Rs,mid+1,r,L,R);
}
int get(int k,int l,int r,int v,LL &w){
    if (l==r){
        if (h[k]>=v) return l-1;
        else{w+=f[k];return l;}
    }
    if (tg[k]) down(k,l,r);
    if (h[Rs]>=v) return get(Ls,l,mid,v,w);
    else{
        w+=f[Ls];
        return get(Rs,mid+1,r,v,w);
    }
}
void work(int l,int r,int v){
    if (l>r) return;
    LL x=1ll*(r-l+1)*(r-l+2)/2;
    t=0;find(1,1,n,l,r);
    LL y=0;int u=r;
    for (int i=1;i<=t;i++){
        if (h[S[i].k]>=r){
            if (i>1) u=get(S[i-1].k,S[i-1].l,S[i-1].r,r,y);
            else u=l-1; break;
        }
        if (i>1) y+=f[S[i-1].k];
        if (i==t) u=get(S[i].k,S[i].l,S[i].r,r,y);
    }
    x-=(y+1ll*(r-u)*r-1ll*(l+r)*(r-l+1)/2+(r-l+1));
    if (l<=u) upd(1,1,n,l,u,r);s+=x*v;
}
void work(){
    scanf("%d",&n);
    build(1,1,n);m=0;s=0;
    for (int i=1;i<=n;i++)
        scanf("%d",&a[i]),
        b[a[i]]=i,m=max(m,a[i]);
    for (int v,i=m;i;i--){
        v=0;
        for (int j=i;j<=m;j+=i)
            if (b[j]) g[++v]=b[j];
        if (v<2) continue;
        sort(g+1,g+v+1);
        work(g[1]+1,g[v]-1,i);
        work(1,g[v-1]-1,i);
        work(g[2]+1,n,i);
    }
    for (int i=1;i<=m;i++) b[i]=0;
    printf("%lld\n",s);
}
int main(){
    for (scanf("%d",&T);T--;work());
    return 0;
}

 

posted @ 2020-02-03 17:31  xjqxjq  阅读(104)  评论(0编辑  收藏  举报