P5610 解题报告

P5610 解题报告

简要题意

一个长为 \(n\)非负整数序列 \(a\),支持以下两个操作:

  • 1 l r x:把区间 \([l,r]\) 中所有 \(x\) 的倍数除以 \(x\)
  • 2 l r:查询区间 \([l,r]\) 的和。

本题强制在线。

数据范围: \(1\leq n,m\leq 10^5\)\(0\leq a_i\leq 5\times 10^5\)\(1\leq x\leq 5\times 10^5\)\(1\leq l\leq r\leq n\)

时间限制:\(500\) ms。

分析

首先,我们考虑一个数至多被操作 \((\log V)\) 次,所以我们不需要关心操作部分的时间复杂度。因此我们只需要分析怎么找到需要被操作的数。

我们注意到:在 \([1,5\times 10 ^5]\) 内因数最多的个数为 \(200\)。所以我们开 \(5\times 10^5\) 个链表,然后我们就可以枚举一个数的所有因数,然后把 \(a_i\) 插入到每一个因数的链表中,我们就可以二分查找操作区间的左右端点,然后操作一个点的时候我们就判断这个点是否还是 \(x\) 的因数,如果不是,就把它删掉。删掉的操作可以使用并查集实现。询问操作就用常数最小的树状数组。

但是至此,我们还是做不了这题。我们还要做很多事情:

  • 首先,我们枚举一个数的因数这部分对每一个数做是 \(O(n \sqrt n)\) 的,这并不优秀。我们考虑枚举因数,然后枚举它的倍数,这个时间复杂度是调和级数,即 \(O(n \log n)\)

  • 其次,vector 实在太慢了。我们只好自己实现链表:开一个内存池,然后数据就用指针,赋值之后就可以当普通数组用了。但是注意,这个内存池不要开太大,不然会增大常数(指针移动的时间)。

于是,我们整理思路,总结一下步骤:

  • 开桶,然后预处理出每一个因数的出现次数,然后预留出每一个因数的链表空间;统计每一个数的因数个数并预留空间。

  • 然后预处理每一个数的因数,然后对每一个序列中的元素插入因数。

  • 在询问时,先二分出当前询问区间对应到当前链表上的区间,然后通过并查集实现访问下一项,每一次访问下一项时判断当前数是否还是当前数的倍数,不是就通过并查集删掉。

代码

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define Inf (1ll<<60)
#define For(i,s,t) for(int i=s;i<=t;++i)
#define Down(i,s,t) for(int i=s;i>=t;--i)
#define ls (i<<1)
#define rs (i<<1|1)
#define bmod(x) ((x)>=p?(x)-p:(x))
#define lowbit(x) ((x)&(-(x)))
#define End {printf("NO\n");exit(0);}
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pii;
inline void ckmx(int &x,int y){x=(x>y)?x:y;}
inline void ckmn(int &x,int y){x=(x<y)?x:y;}
inline void ckmx(ll &x,ll y){x=(x>y)?x:y;}
inline void ckmn(ll &x,ll y){x=(x<y)?x:y;}
inline int min(int x,int y){return x<y?x:y;}
inline int max(int x,int y){return x>y?x:y;}
inline ll min(ll x,ll y){return x<y?x:y;}
inline ll max(ll x,ll y){return x>y?x:y;}
char buf[1<<20],*p1,*p2;
#define gc() (p1 == p2 ? (p2 = buf + fread(p1 = buf, 1, 1 << 20, stdin), p1 == p2 ? EOF : *p1++) : *p1++)
#define read() ({\
    int x = 0, f = 1;\
    char c = gc();\
    while(c < '0' || c > '9') f = (c == '-') ? -1 : 1, c = gc();\
    while(c >= '0' && c <= '9') x = x * 10 + (c & 15), c = gc();\
    f * x;\
})
void write(int x){
    if(x>=10) write(x/10);
    putchar(x%10+'0');
}
const int N=1e5+100,V=5e5+100,K=180;
int n,T,m,a[N],*p[V],*b[V],c[V],ctp[V];
ull vl[N];
int pf_p[21496900],*tp_p=pf_p;
int pf_lst[21496900],*tp_lst=pf_lst;
int pf_DSU[21496900],*tp_DSU=pf_DSU;
inline void add(int x,int v){
    // printf("(%d %d)\n",x,v);
    while(x<=n) vl[x]+=v,x+=lowbit(x);
}
inline ll ask(int x){
    ll res=0;
    while(x) res+=vl[x],x-=lowbit(x);
    return res;
}
struct DSU{
    int *f;
    inline void init(int n){
        for(int i=0;i<n;++i)
            f[i]=i;
    }
    inline int find(int x){
        return f[x]^x ? (f[x]=find(f[x])) : x;
    }
}d[V];
inline void solve(int l,int r,int x){
    if(x==1) return;
    l=lower_bound(b[x],b[x]+c[x],l)-b[x];
    r=upper_bound(b[x],b[x]+c[x],r)-b[x]-1;
    if(l>r) return;
    // printf("? %d %d\n",l,r);fflush(stdout);
    int nw=d[x].find(l);
    // printf("! %d\n",nw);fflush(stdout);
    while(nw<=r){
        int id=b[x][nw];
        // printf("! %d\n",id);fflush(stdout);
        if(!(a[id]%x))
            add(id,a[id]/x-a[id]),a[id]/=x;
        if(nw>=r) break;
        if(a[id]%x)
            d[x].f[nw]=nw+1;
        nw=d[x].find(nw+1);
    }
}
int main()
{
#if !ONLINE_JUDGE
    freopen("test.in","r",stdin);
    freopen("test.out","w",stdout);
#endif 
    n=read(),T=read();
    For(i,1,n) a[i]=read(),add(i,a[i]),m=max(m,a[i]),++c[a[i]];
    For(i,2,m)
        for(int j=(i<<1);j<=m;j+=i)
            c[i]+=c[j],++ctp[j];
    For(i,2,m)
        if(c[i]){
            d[i].f=tp_DSU;
            d[i].init(c[i]);
            tp_DSU+=c[i];
            b[i]=tp_lst;
            tp_lst+=c[i];
            c[i]=0;
        }
    For(i,2,m){
        p[i]=tp_p;
        ++ctp[i],tp_p+=ctp[i],ctp[i]=0;
    }
    For(i,2,m)
        for(int j=i;j<=m;j+=i)
            p[j][ctp[j]]=i,++ctp[j];
    int v;
    For(i,1,n)
        for(int j=0;j<ctp[a[i]];++j)
            v=p[a[i]][j],b[v][c[v]]=i,++c[v];
    int opt,l,r,x;
    ull lstans=0;
    while(T--){
        opt=read();
        if(opt==1){
            l=read()^lstans,r=read()^lstans,x=read()^lstans;
            solve(l,r,x);
        }
        else{
            l=read()^lstans,r=read()^lstans;
            printf("%lld\n",lstans=(ask(r)-ask(l-1)));
        }
    }
    return 0;
}
posted @ 2025-11-07 18:41  XiaoZi_qwq  阅读(1)  评论(0)    收藏  举报