1. 程式人生 > >【BZOJ 1129】[POI2008]Per 二叉堆

【BZOJ 1129】[POI2008]Per 二叉堆

這一 gin null tree bst reg 二叉堆 頻繁 ons

這個東西讀完題之後,就能知道我們要逐位計算貢獻.
推一下式子,會發現,這一位的貢獻,是當前剩余的數字形成的序列的總數,乘上所剩數字中小於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 二叉堆