终于过了,感觉还是有点东西的。
首先我们有一个很好想的 \(O(n(\ln A+\sqrt{A})\log n)\) 的做法。首先这个式子能写成 \(p_i=\sum\limits_{j=1}^i\sum\limits_{k=1}^i\left(a_j-a_k\left\lfloor\dfrac{a_j}{a_k}\right\rfloor\right)\) 的形式。前面求和那部分是简单的,我们主要去想怎么求 \(\sum\limits_{j=1}^i\sum\limits_{k=1}^i\left(a_k\left\lfloor\dfrac{a_j}{a_k}\right\rfloor\right)\)。思考这个在 \(p_{i-1}\) 转移到 \(p_i\) 时是怎么变化的:不难发现新增的贡献可以看成两部分:\(\sum\limits_{j=1}^{i-1}\left(a_i\left\lfloor\dfrac{a_j}{a_i}\right\rfloor\right)\) 和 \(\sum\limits_{j=1}^{i-1}\left(a_j\left\lfloor\dfrac{a_i}{a_j}\right\rfloor\right)\)。前面的可以枚举 \(\left\lfloor\dfrac{a_j}{a_i}\right\rfloor\) 的值,后面的可以整除分块,再加上一个 BIT 数数的 \(\log n\),总时间复杂度 \(O(n(\ln A+\sqrt{A})\log n)\)(\(\ln n\) 是因为 \(a_i\) 两两不同)。
显然,这玩意是过不了的(Time limit exceeded on test 7),思考怎么优化。发现都是整除分块 \(\sqrt{n}\) 的复杂度拖慢了我们的速度,如果我们能用类似求前面一部分的方法去解决后面一部分就可以做到 \(O(n\ln n\log n)\) 了。那么,能做到吗?
假设当前枚举到 \(a_j\),考虑它会让后面的 \(a_i\) 对它产生多少贡献。可以枚举 \(\left\lfloor\dfrac{a_i}{a_j}\right\rfloor=x\),那么当 \(a_i\) 在 \([x\cdot a_j,(x+1)\cdot a_j)\) 范围内时就会有 \(x\cdot a_j\) 的影响,这就是一个区间加单点求和,也是 BIT 的拿手好戏。总时间复杂度 \(O(n\ln n\log n)\)。
题外话:BIT 判边界好麻烦,我用的线段树。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=1e18,SIZ=3e5;
inline int read(){
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
return x*f;
}
struct segtree{
#define ls p<<1
#define rs p<<1|1
#define lson l,mid,ls
#define rson mid+1,r,rs
struct Node{
int s,tag;
}c[1210005];
void pushup(int p){
c[p].s=c[ls].s+c[rs].s;
}
void pushdown(int l,int r,int p){
if(!c[p].tag)return;
int siz=r-l+1,ln=siz-(siz>>1),rn=siz>>1;
c[ls].tag+=c[p].tag,c[rs].tag+=c[p].tag;
c[ls].s+=ln*c[p].tag,c[rs].s+=rn*c[p].tag;
c[p].tag=0;
}
void build(int l,int r,int p){
c[p].tag=0;
if(l==r){c[p].s=0;return;}
int mid=(l+r)>>1;
build(lson),build(rson);
pushup(p);
}
void update(int l,int r,int p,int L,int R,int k){
if(L>R)return;
if(L<=l&&r<=R){c[p].s+=(r-l+1)*k,c[p].tag+=k;return;}
int mid=(l+r)>>1;pushdown(l,r,p);
if(L<=mid)update(lson,L,R,k);
if(R>mid)update(rson,L,R,k);
pushup(p);
}
int query(int l,int r,int p,int L,int R){
if(L>R)return 0;
if(L<=l&&r<=R)return c[p].s;
int mid=(l+r)>>1,res=0;pushdown(l,r,p);
if(L<=mid)res+=query(lson,L,R);
if(R>mid)res+=query(rson,L,R);
return res;
}
#undef ls
#undef rs
#undef lson
#undef rson
}Tr1,Tr2;
int a[200005];
signed main(){
int n=read();Tr1.build(0,SIZ,1),Tr2.build(0,SIZ,1);
for(int i=1;i<=n;i++)a[i]=read();
for(int i=1,sum=0,res=0;i<=n;i++){
res+=sum+a[i]*(i-1);res-=Tr2.query(0,SIZ,1,a[i],a[i]);
for(int j=0;j*a[i]<=SIZ;j++)res-=Tr1.query(0,SIZ,1,j*a[i],min(SIZ,(j+1)*a[i]-1))*j*a[i];
for(int j=0;j*a[i]<=SIZ;j++)Tr2.update(0,SIZ,1,j*a[i],min(SIZ,(j+1)*a[i]-1),j*a[i]);
sum+=a[i];Tr1.update(0,SIZ,1,a[i],a[i],1);
printf("%lld ",res);
}
return 0;
}