[cqoi2016]伪光滑数 解题报告

这题有点意思。。
考虑对于i个质因子,最大的质因子至多为j能生成的数。我们需要每次在其中取最大值,显然它可以用可持久化左偏树来维护。有 leftist(i,j)=leftist(i1,j)j[jin]+leftist(i,j1)
然后我们再用一个堆来维护所有可持久化左偏树的根的最小值。

= =膜拜一下大爷的做法(妈的为什么我做法总是这么傻逼!!):
左偏树的话时间复杂度当然是 O(KlogK+(AlogAlogN)2)(A=128) .
但如果我们把左偏树改成暴力减小每个质因子然后全插进堆里的话。。时间复杂度是 O(KlogK 质因子个数 ) ,质因子个数最大是16,实际情况应该在4~5左右。所以直接暴力即可。

#include<cstdio>
#include<iostream>
using namespace std;
#include<cstring>
#include<cmath>
#include<algorithm>
typedef long long LL;
const LL N=1e18+5;
const int K=8e5+5;
const int A=128;

const int P=35;
const int Log=60;
int prime[P];
bool p[A+5];

const int Left=1e6;
struct LS{
    int ls,rs;
    int dis;
    LL flag;
    LL data;
}leftist[Left];
int ltot=2;
int root[Log+5][P];
void out(int node){
    printf("leftist[%d]={ls=%d,rs=%d,dis=%d,flag=%I64d,data=%I64d}\n",node,leftist[node].ls,leftist[node].rs,leftist[node].dis,leftist[node].flag,leftist[node].data);
}
void paint(int &node,LL flag){
    if(node){
        leftist[ltot]=leftist[node];
        node=ltot++;
        leftist[node].flag*=flag;
        leftist[node].data*=flag;
    }
}
void pushdown(int node){
    if(leftist[node].flag!=1){
        paint(leftist[node].ls,leftist[node].flag),paint(leftist[node].rs,leftist[node].flag);
        leftist[node].flag=1;
    }
}
void merge(int &node,int u,int v){
    //printf("Merge(%d,%d)\n",u,v);
    if(!u&&!v){
        node=0;
        return;
    }
    if(!u){
        node=v;
        return;
    }
    if(!v){
        node=u;
        return;
    }
    node=ltot++;
    if(leftist[u].data<leftist[v].data)swap(u,v);
    leftist[node]=leftist[u];
    pushdown(node);
    merge(leftist[node].rs,leftist[node].rs,v);
    if(leftist[leftist[node].rs].dis>leftist[leftist[node].ls].dis)swap(leftist[node].ls,leftist[node].rs);
    leftist[node].dis=leftist[leftist[node].rs].dis+1;
}

int heap[P*Log+K],htot=1;
void out(){
    for(int i=1;i<htot;++i)printf("%d ",heap[i]);
    puts("");
}
bool less_heap(int a,int b){
    return leftist[heap[a]].data<leftist[heap[b]].data;
}
void down(int node){
    for(int next=node<<1;next<htot;node=next,next<<=1){
        if(next+1<htot&&less_heap(next,next+1))++next;
        if(less_heap(next,node))return;
        swap(heap[node],heap[next]);
    }
}
void up(int node){
    for(int next=node>>1;next&&less_heap(next,node);node=next,next>>=1)swap(heap[node],heap[next]);
}

void add(int node){
    if(node){
        heap[htot]=node;
        up(htot++);
    }
}
int main(){
    freopen("bzoj_4524.in","r",stdin);
    freopen("bzoj_4524.out","w",stdout);

    for(int i=2;i<A;++i){
        if(!p[i])prime[++prime[0]]=i;
        for(int j=1;j<=prime[0]&&i*prime[j]<A;++j){
            p[i*prime[j]]=1;
            if(i%prime[j]==0)break;
        }
    }

    LL n,tmp;
    int k;
    cin>>n>>k;

    leftist[1]=(LS){0,0,1,1,1};
    for(int j=0;j<=prime[0];++j)root[0][j]=1;
    int u,v;
    for(int j=1,i;j<=prime[0];++j){
        tmp=n;
        for(i=1;tmp>=prime[j];++i,tmp/=prime[j]){
            u=root[i-1][j];
            paint(u,prime[j]);
            v=root[i][j-1];
            //cout<<"("<<i<<','<<j-1<<")="<<v<<endl;
            merge(root[i][j],u,v);
            //printf("root(%d,%d)=",i,j);
            //out(root[i][j]);
        }
        for(;i<=Log;++i)root[i][j]=root[i][j-1];
    }
    for(int i=1;i<=Log;++i)
        if(root[i][prime[0]]){
            //cout<<"Get("<<i<<","<<prime[0]<<")\n";
            heap[htot++]=root[i][prime[0]];
        }

    for(int i=htot;--i;)down(i);
    //out();

    while(--k){
        pushdown(heap[1]);
        if(leftist[heap[1]].ls)add(leftist[heap[1]].ls);
        if(leftist[heap[1]].rs)add(leftist[heap[1]].rs);
        heap[1]=heap[--htot];
        down(1);
        //out();

        if(htot==1){
            puts("No answer");
            return 0;
        }
    }

    cout<<leftist[heap[1]].data<<endl;
}

总结:
①一个数的质因子个数是很少的!

你可能感兴趣的:(heap,左偏树)