• 【BZOJ 1129】[POI2008]Per 二叉堆


    这个东西读完题之后,就能知道我们要逐位计算贡献.
    推一下式子,会发现,这一位的贡献,是当前剩余的数字形成的序列的总数,乘上所剩数字中小于s上这一位的数的个数与所剩数字的总数的比.
    所以我们维护“当前剩余的数字形成的序列的总数”以及权值数组的前缀和就好了.
    后者可以用树状数组维护,前者可以用一个变量维护.
    但是!!!!!模数不是质数!!!!!
    这就很坑爹,网上的人基本上都是把模数质因数分解后,对于每一种质因数计算一次答案,最后再crt计算答案.
    然而,作为一只**,我用长得像二叉堆,但是维护信息上又有点像平衡树的二叉树维护变量,直接以m为模数计算出答案.
    我的思路是这样的,我们维护数字,只有乘和除,既然不能算逆元,那么我们就维护这个数字的所有可能含有的质数的个数,并且对于所有的质数建立树形结构,每个点除了维护其自己信息以外,还维护了其子树乘积,那么树根的子树乘积就是这个数,而我们乘(除)一个数的时候,将乘(除)的数质因子拆分,对于每个质因子,修改他在树中的信息以及他的在树中的祖先的信息,这样的复杂度是O(nlog^2n)的.
    一开始我直接按照质数的大小建立BST,卡了半天常数才过,后来发现,我不如按照质数大小建立小根堆,这样使用频繁的质数(小的质数)的深度就会变小,于是我一下子从bzoj倒数第一滚到大众时间.

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define R register
    typedef long long LL;
    char xB[(1<<15)+10],*xS,*xT;
    #define gtc (xS==xT&&(xT=(xS=xB)+fread(xB,1,1<<15,stdin),xS==xT)?0:*xS++)
    inline void read(int &x){
      R char ch=gtc;
      for(x=0;ch<'0'||ch>'9';ch=gtc);
      for(;ch>='0'&&ch<='9';x=(x*10)+ch-'0',ch=gtc);
    }
    const int N=300000;
    inline int gcd(int x,int y){return !x?y:gcd(y%x,x);}
    int len,prime[N/10+10],min[N+10],size[N/10+10];
    bool isnot[N+10];
    int n,m,ans,P,lim;
    int mii[N*10+10],*begin[N/10+10];
    #define mi(a,b) (*(begin[(a)]+(b)))
    int a[N+10],tree[N+10],cnt[N+10];
    inline void U(R int pos,int key){
      for(;pos<=N;pos+=pos&(-pos))
        tree[pos]+=key;
    }
    inline int Q(R int pos){
      R int ret=0;
      for(;pos>0;pos-=pos&(-pos))
        ret+=tree[pos];
      return ret;
    }
    inline void get(R int x,int opt){
      while(min[x])
        size[min[x]]+=opt,x/=prime[min[x]];
    }
    struct BST{
      BST *ch[2],*f;
      int id,key;
    }node[N+10],*root;
    #define pushup(p) (p->key=(LL)mi(p->id,size[p->id])*p->ch[0]->key%P*p->ch[1]->key%P)
    #define mid ((l+r)>>1)
    inline void build(BST *&p,BST *fa,int id){
      p=node+id,p->key=1;
      if(id>len)return;
      p->f=fa,p->id=id;
      build(p->ch[0],p,id<<1);
      build(p->ch[1],p,(id<<1)|1);
      pushup(p);
    }
    inline void update(int x){
      R BST *p=node+x;
      while(p)pushup(p),p=p->f;
    }
    inline void update(R int x,int opt){
      if(x==1)return;
      R int last=0;
      while(min[x]){
        size[min[x]]+=opt;
        if(last&&min[x]!=last)update(last);
        last=min[x];
        x/=prime[min[x]];
      }
      update(last);
    }
    int main(){
      //freopen("rio.in","r",stdin);
      R int i,j;
      read(n),read(P),lim=n;
      isnot[1]=true,min[1]=0;
      for(i=2;i<=lim;++i){
        if(!isnot[i])prime[++len]=i,min[i]=len;
        for(j=1;prime[j]*i<=lim;++j){
          isnot[prime[j]*i]=true;
          min[prime[j]*i]=j;
          if(i%prime[j]==0)break;
        }
      }
      for(i=1;i<=n;++i)read(a[i]),U(a[i],1),++cnt[a[i]];
      for(i=1;i<=n;++i)get(i,1);
      for(i=1;i<=len;++i){
        begin[i]=mii+m;
        m+=size[i]+20+1;
        mi(i,0)=1;
        for(j=1;j<=size[i]+20;++j)
          mi(i,j)=(LL)mi(i,j-1)*prime[i]%P;
      }
      for(i=1;i<=N;++i)
        for(j=2;j<=cnt[i];++j)
          get(j,-1);
      build(root,NULL,1);
      R int s;
      for(i=1;i<=n;++i){
        s=Q(a[i])-cnt[a[i]];
        if(s){
          update(s/gcd(s,n-i+1),1),update((n-i+1)/gcd(s,n-i+1),-1);
          ans=(ans+root->key)%P;
          update(cnt[a[i]],1),update(s,-1);
        }else{
          update(cnt[a[i]],1),update(n-i+1,-1);
        }
        --cnt[a[i]],U(a[i],-1);
      }
      ans=(ans+1)%P;
      printf("%d
    ",ans);
      return 0;
    }
  • 相关阅读:
    硬件重定向
    ARM处理器模式切换(含MRS,MSR指令)
    ARM处理器工作模式
    ARM处理器异常处理
    JS和H5做一个音乐播放器,附带源码
    php页面zend加密乱码的解决办法
    ZendOptimizer怎么安装?Php网站打开显示乱码
    PHP和Python如何选择?或许可以考虑这三个问题
    SQL Server 2008读书笔记(3):表
    Dijkstra算法
  • 原文地址:https://www.cnblogs.com/TSHugh/p/8568221.html
Copyright © 2020-2023  润新知