• [洛谷P3987]我永远喜欢珂朵莉~


    题目大意:

    给你一个数列。有两个操作:

    1. 给一个区间内所有(v_i)的倍数除以(v_i)。
    2. 询问区间和。

    解题思路:

    首先,建(10^6)棵平衡树(不要慌o((⊙﹏⊙))o)

    然后对操作离线。

    对于每个初始的数(a_i),暴算出他的所有约数,并把(i)丢进与约数相应的平衡树里(如果修改中没出现这个约数则忽略即可)。

    由于(10^6)内约数最多的数也只有200来个约数,所以不用担心空间问题。

    接下来处理操作。

    对于每个修改,我们直接在相应的平衡树中找到修改区间内的数,然后若(a_i)是v的倍数,则(a_i)除以v,若除完后(a_i)不是(v)的倍数了(或本身已经不是了),则在这个平衡树中删除这个数。

    注意本身可能已经在其他v的修改中除掉了某个约数,所以仍然要判是否整除。

    区间求和什么的,树状数组即可。

    C++ Code:

    #pragma GCC optimize("Ofast")
    #pragma GCC optimize("unroll-loops")
    #pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
    #include<bits/stdc++.h>
    #define reg register
    using LoveLive=long long;
    const int M=1e6+5,N=1e5+5;
    LoveLive btr[M];
    int n,m,a[N],opt[N],L[N],R[N],v[N],cnt=0,rt[M],nodes=0;
    std::vector<int>V[1000005];std::stack<int>st;
    struct node{
        int ls,rs,r,v,sz;
    }d[M<<6];
    bool vis[M];
    inline void add(int x,int d){for(;x<=M;x+=x&-x)btr[x]+=d;}
    inline LoveLive ask(int x){reg LoveLive r=0;for(;x;x-=x&-x)r+=btr[x];return r;}
    inline int readint(){
        reg int c=getchar(),d=0;
        for(;!isdigit(c);c=getchar());
        for(;isdigit(c);c=getchar())
        d=(d<<3)+(d<<1)+(c^'0');
        return d;
    }
    inline int merge(int x,int y){
        if(!x||!y)return x|y;
        if(d[x].r<d[y].r){
            d[x].rs=merge(d[x].rs,y);
            return x;
        }
        d[y].ls=merge(x,d[y].ls);
        return y;
    }
    inline void split(int u,int k,int&x,int&y){
        if(!u)return void(x=y=0);
        if(d[u].v<=k)
        split(d[x=u].rs,k,d[u].rs,y);else
        split(d[y=u].ls,k,x,d[u].ls);
    }
    inline void Delete(int&rt,int x){
        reg int a,b;
        split(rt,x-1,rt,a);
        split(a,x,a,b);
        rt=merge(rt,b);
    }
    int build(int l,int r,int&i){
        if(l>r)return 0;
        reg int nw=++nodes,mid=l+r>>1;
        d[nw]=(node){0,0,rand(),V[i][mid]};
        d[nw].ls=build(l,mid-1,i);
        d[nw].rs=build(mid+1,r,i);
        return nw;
    }
    void dfs(int now,int&v){
        if(!now)return;
        dfs(d[now].ls,v),dfs(d[now].rs,v);
        int&nw=a[d[now].v];
        if(nw%v==0){
            add(d[now].v,nw/v-nw);
            nw/=v;
        }
        if(nw%v)st.push(d[now].v);
    }
    int main(){
        srand(time(0));
        srand(rand());
        n=readint(),m=readint();
        for(reg int i=1;i<=n;++i)add(i,a[i]=readint());
        for(reg int i=1;i<=m;++i){
            opt[i]=readint(),L[i]=readint(),R[i]=readint();
            if(opt[i]==1)vis[v[i]=readint()]=1;
        }
        //begin 寻找每个数的约数并将每个数的位置插入对应的平衡树 
        for(reg int i=1;i<=n;++i){
            reg int&nw=a[i];
            for(reg int j=1;j*j<=a[i];++j)
            if(nw%j==0){
                if(vis[j]&&j>1)V[j].push_back(i);
                if(j*j<nw&&vis[nw/j])V[nw/j].push_back(i);
            }
        }
        for(reg int i=2;i<M;++i)
        if(V[i].size())rt[i]=build(0,V[i].size()-1,i);
        //end 
        //begin 查询及修改 
        for(reg int i=1;i<=m;++i)
        if(opt[i]==1){
            if(v[i]>1){
                reg int&nw=rt[v[i]],x,y;
                split(nw,L[i]-1,nw,x);
                split(x,R[i],x,y);
                dfs(x,v[i]);
                nw=merge(merge(nw,x),y);
                while(!st.empty()){
                    reg int p=st.top();
                    st.pop();
                    split(nw,p-1,nw,x);
                    split(x,p,x,y);
                    nw=merge(nw,y);
                }
            }
        }else printf("%lld
    ",ask(R[i])-ask(L[i]-1));
        //end 
        return 0;
    }
  • 相关阅读:
    JavaScript 垃圾回收
    JavaScript 跳坑指南
    javaScript AJAX
    高效 JavaScript
    Java使用 Thumbnails 压缩图片
    Vue前端压缩图片
    JS input输入框字数超出长度显示省略号.....
    Vue图片浏览组件vviewer使用
    浏览器获取京东cookie
    图片在容器内水平垂直居中显示
  • 原文地址:https://www.cnblogs.com/Mrsrz/p/9385628.html
Copyright © 2020-2023  润新知