[洛谷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\n",ask(R[i])-ask(L[i]-1));
    //end 
    return 0;
}

 

posted @ 2018-07-29 16:05  Mrsrz  阅读(511)  评论(0编辑  收藏