【BZOJ 1129】[POI2008]Per 二叉堆

這個東西讀完題以後,就能知道咱們要逐位計算貢獻.
推一下式子,會發現,這一位的貢獻,是當前剩餘的數字造成的序列的總數,乘上所剩數字中小於s上這一位的數的個數與所剩數字的總數的比.
因此咱們維護「當前剩餘的數字造成的序列的總數」以及權值數組的前綴和就行了.
後者能夠用樹狀數組維護,前者能夠用一個變量維護.
可是!!!!!模數不是質數!!!!!
這就很坑爹,網上的人基本上都是把模數質因數分解後,對於每一種質因數計算一次答案,最後再crt計算答案.
然而,做爲一隻**,我用長得像二叉堆,可是維護信息上又有點像平衡樹的二叉樹維護變量,直接以m爲模數計算出答案.
個人思路是這樣的,咱們維護數字,只有乘和除,既然不能算逆元,那麼咱們就維護這個數字的全部可能含有的質數的個數,而且對於全部的質數創建樹形結構,每一個點除了維護其本身信息之外,還維護了其子樹乘積,那麼樹根的子樹乘積就是這個數,而咱們乘(除)一個數的時候,將乘(除)的數質因子拆分,對於每一個質因子,修改他在樹中的信息以及他的在樹中的祖先的信息,這樣的複雜度是O(nlog^2n)的.
一開始我直接按照質數的大小創建BST,卡了半天常數才過,後來發現,我不如按照質數大小創建小根堆,這樣使用頻繁的質數(小的質數)的深度就會變小,因而我一會兒從bzoj倒數第一滾到大衆時間.node

#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;
}
相關文章
相關標籤/搜索