洛谷3987之我的解法

首先正如官方题解所述“瓶颈在于如何找出所有该被/的数而不在于如何维护”。
考虑离线来完成这一步。对于每个操作 (id=l,r,x) ,在 l 处添加x(具体操作是用一个set<int> e[A+5];,并e[x].insert(id))然后在 r+1 处添加 x 的删除标记。这样就可以知道每个数受哪些询问的影响。
然后对于一个ai,将它的因子(在500000以内最多200个)对于的set<int>*begin()取出来,以询问的先后顺序为关键字建立一个堆。然后模拟 ai 不断除到1的情况,如果当前堆顶的数对应的x仍然是 ai 的因子,那么就把堆顶的迭代器++后扔回堆中,否则弹掉堆顶,这样做预处理时n*(建立大小为d的堆的时间+d次pop+(logw等于19)次push)的复杂度。
如果直接用普通的堆,那么是 ndlogd 。实测常数不是太大的话,是能AC的,尽管出题人扬言有空了要卡掉这种算法
注意到pop的次数远大于push的次数,考虑快速( O(n) )建立,快速( O(1) )pop,慢速(单次 O(logn) )push的数据结构。
然而这是不存在的,因为基于比较的排序是 O(nlogn) 的,而值域又是500000。
如果不考虑快速建立,那么平衡树是不错的选择,对于每个节点维护指向前驱后继的指针即可。
其实平衡树在原序列有序的情况下也能 O(n) 建立,然而每次直接sort会多log。
对于这一点,把所有元素离线下来(离线套离线,离线算法的子算法仍要离线,有趣),然后基数排序即可。
就这样,基数排序+平衡树+set+vector+树状数组总算达到了std的复杂度,然而已经不想写了。
据说原始平衡树版本的std有4到5K,可能不比我这种算法好多少?
可是分块版本的std又快又短。
我这种算法也只能是娱乐大众了。
下面是我原始的 ndlogd 的代码

#pr\
agma GCC optimize("O2")
#include<vector>
#include<set>
#include<cstdio>
#include<cctype>
#include<queue>
using namespace std;
typedef long long ll;
inline void read(int&x){
    char c=getchar();
    for(;!isdigit(c);c=getchar());
    for(x=0;isdigit(c);c=getchar())x=x*10+c-48;
}
const int N=100005,A=500000;
vector<int> d[A+5],b[N];
vector<pair<int,int> > op[N];
set<int> e[A+5];
set<int>::iterator it;
int n,i,m,l[N],r[N],x[N],o[N],a[N],y,j,k,z;
ll s[N];
inline void add(int i,int x){for(;i<=n;i+=i&-i)s[i]+=x;}
inline ll query(int i){register ll ans=0;for(;i;i-=i&-i)ans+=s[i];return ans;}
struct node{
    int id;
    set<int>::iterator it;
    bool operator<(const node&rhs)const{
        return *it>*rhs.it;
    }
}xx;
struct pq{
    static const int N=450;
    int l[N],r[N],rt,d[N],q[N],t,w,t2;
    node v[N];
    int merge(int x,int y){
        if(!x || !y)return x|y;
        if(v[x]<v[y])t2=x,x=y,y=t2;
        r[x]=merge(r[x],y);
        if(d[r[x]]>d[l[x]])t2=l[x],l[x]=r[x],r[x]=t2;
        d[x]=d[r[x]]+1;
        return x;
    }
    inline void ini(){t=1,w=0;}
    inline void pb(const node&x){
        v[++w]=x;l[w]=r[w]=0;d[w]=1;q[w]=w;
    }
    inline void ini2(){
        for(;t<w;t+=2)q[++w]=merge(q[t],q[t+1]);rt=q[w];
    }
    const node& top(){return v[rt];}
    inline void pop(){rt=merge(l[rt],r[rt]);}
    inline bool empty(){return !rt;}
    inline bool push(const node&x){
        v[++w]=x;l[w]=r[w]=0;d[w]=1;rt=merge(rt,w);
    }
}qq;
int main(){
    //freopen("1.txt","r",stdin);freopen("2.txt","w",stdout);
    read(n),read(m);
    for(i=1;i<=n;++i)read(a[i]),add(i,a[i]);
    for(i=2;i<=A;++i)
        for(j=i;j<=A;j+=i)d[j].push_back(i);
    for(i=1;i<=m;++i){
        read(o[i]);read(l[i]);read(r[i]);
        if(o[i]==1)read(x[i]),b[l[i]].push_back(i),b[r[i]+1].push_back(i);
    }
    for(i=1;i<=n;++i){
        for(j=0;j<(int)b[i].size();++j){
            y=b[i][j];
            if(i==l[y])e[x[y]].insert(y);
                else e[x[y]].erase(y);
        }
        qq.ini();
        for(j=0;j<(int)d[a[i]].size();++j){
            y=d[a[i]][j];
            if(!e[y].empty())qq.pb((node){y,e[y].begin()});
        }
        qq.ini2();
        for(y=a[i];!qq.empty() && y>1;){
            xx=qq.top();qq.pop();
            it=xx.it;j=*it;
            if(y%x[j]==0){
                y/=x[j];
                op[j].push_back(make_pair(i,y));
                if(++it!=e[xx.id].end())qq.push((node){xx.id,it});
            }
        }
    }
    for(i=1;i<=m;++i){
        for(j=0;j<(int)op[i].size();++j){
            y=op[i][j].first,z=op[i][j].second;
            add(y,z-a[y]);a[y]=z;
        }
        if(o[i]==2)printf("%lld\n",query(r[i])-query(l[i]-1));
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值