码迷,mamicode.com
首页 > 其他好文 > 详细

【BZOJ 1129】[POI2008]Per 二叉堆

时间:2018-03-14 17:01:46      阅读:224      评论:0      收藏:0      [点我收藏+]

标签:char   style   return   zoj   oid   log   algo   --   turn   

这个东西读完题之后,就能知道我们要逐位计算贡献.
推一下式子,会发现,这一位的贡献,是当前剩余的数字形成的序列的总数,乘上所剩数字中小于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\n",ans);
  return 0;
}

 

【BZOJ 1129】[POI2008]Per 二叉堆

标签:char   style   return   zoj   oid   log   algo   --   turn   

原文地址:https://www.cnblogs.com/TSHugh/p/8568221.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!