[CF920F]SUM and REPLACE:线性筛+线段树+均摊复杂度

题意:

维护一个数列,支持:
1、区间内所有的数变为其约数个数。
2、区间求和。
(1<=n,m<=3e5,1<=ai<=1e6)

分析:

首先我们发现,一个数变为其约数个数最悲观也会减小为原来的一半左右,所以一个数最多只会改变log1e6=16次就会变为1或2。
这里我们用到的均摊复杂度的思想可参考BZOJ3211GSS3
因此,我们对这个数列建一棵线段树,维护区间sum和max。
修改时要递归到叶节点,但若一个区间的max<=2那么直接跳过(因为D(1)=1,D(2)=2)。
这样就实现了均摊复杂度。
约数个数函数可线性筛预处理筛出来即可(不懂的看代码或自行百度约数个数定理)。

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
inline int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch==-'-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return x*f;
}
const int MAXN=300005;
const int MAXNUM=1000005;
int n,d[MAXNUM],cnt[MAXNUM],prm[MAXNUM],tot;
bool vis[MAXNUM];
int a[MAXN],maxn[MAXN<<2],ql,qr;
LL sum[MAXN<<2];
void pre_process(){
    d[1]=1,cnt[1]=0;
    for(int i=2;i<=n;i++){
        if(!vis[i]){
            prm[++tot]=i;
            d[i]=2;
            cnt[i]=1;
        }
        for(int j=1;j<=tot&&prm[j]<=n/i;j++){
            vis[i*prm[j]]=1;
            if(i%prm[j]==0){
                cnt[i*prm[j]]=cnt[i]+1;
                d[i*prm[j]]=d[i]/(cnt[i]+1)*(cnt[i*prm[j]]+1);
                break;
            }
            cnt[i*prm[j]]=1;
            d[i*prm[j]]=d[i]*2;
        }
    }
}
#define mid ((l+r)>>1)
#define lc (o<<1)
#define rc ((o<<1)|1)
void build(int o,int l,int r){
    if(l==r){
        sum[o]=maxn[o]=a[l];
        return;
    }
    build(lc,l,mid);
    build(rc,mid+1,r);
    maxn[o]=max(maxn[lc],maxn[rc]);
    sum[o]=sum[lc]+sum[rc];
}
void upd(int o,int l,int r){
    if(maxn[o]<=2) return;
    if(l==r){
        sum[o]=maxn[o]=d[maxn[o]];
        return;
    }
    if(mid>=ql) upd(lc,l,mid);
    if(mid<qr) upd(rc,mid+1,r);
    maxn[o]=max(maxn[lc],maxn[rc]);
    sum[o]=sum[lc]+sum[rc];
}
LL query(int o,int l,int r){
    if(ql<=l&&r<=qr) return sum[o];
    LL ans=0;
    if(mid>=ql) ans+=query(lc,l,mid);
    if(mid<qr) ans+=query(rc,mid+1,r);
    return ans;
}
int main(){
    n=1000005;
    pre_process();
    n=read();int q=read();
    for(int i=1;i<=n;i++) a[i]=read();
    build(1,1,n);
    while(q--){
        int opt=read();
        if(opt==1){
            ql=read(),qr=read();
            upd(1,1,n);
        }
        else{
            ql=read(),qr=read();
            printf("%lld\n",query(1,1,n));
        }
    }
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/ErkkiErkko/p/9301737.html
今日推荐