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