洛谷3987之我的解法

来源:互联网 发布:wifi定位技术算法 编辑:程序博客网 时间:2024/05/22 10:29

首先正如官方题解所述“瓶颈在于如何找出所有该被/的数而不在于如何维护”。
考虑离线来完成这一步。对于每个操作(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;}
原创粉丝点击