PER

PER-Permutation P3477

  • 题目大意:

    一个元素个数为 \(n\) 的多重集的一个排列和 \(m\) ,求这个排列的字典序排名取模 \(m\)
  • 思考:

    • 首先,我们显然能够根据题意想到 康托展开,但它们之间当然是有区别的(这不是废话么) , 本题的排列是一个可重的,范围不定的,不连续的无序排列。
    • 知道了区别,下一步呢?我们仍可以依照康托展开的思想,对排列的每一位进行考虑。
    • 本题的任意模数并不十分友好,因为在这种意义下逆元并不存在,因此考虑将模数分解,对每一个互质于当前答案贡献的模数分别计算,最后合并即可。
  • 实现:

    • 式子:

      • \[ans=\sum^n_{i=1}{\frac{val_{a_i}(n-i)!}{\prod^{col_{a_i}}_j{cnt_j!}}} \]

      • 式子中的 \(a\) 为原排列,\(val_i\) 表小于 \(a_i\) 的,且未访问过的数的个数,\(col_i\) 为原排列中,从 \(i\)\(n\) 的数的种类数,\(cnt\) 则是一个桶。
    • 转化:

      • 因为模数的不确定性,导致逆元无法求,因此我们先将模数 \(p\) 分解为质因数 \(p_i\) ,再把每一个 \(ans_i\) 中存在的 \(p\) 的因数消去,这样也就保证了 \(ans_i\)\(p\) 互质,逆元也就十分好求了。
      • \(ans_i\) 是分数的形式,因此我们要对分子分母分别消去 \(p\) 的质因子,最后我们当然要把消去的质因子再乘回来,而 \(ans_i\) 一定是个整数,也就是说,对于一个 \(p_i\) ,分子中的数量一定比分母中的大,那么消去的(或者说需要乘回来的)质因子的形式也就是 \({p_i}^k(k\in \mathbb{Z})\)
      • 我们需要阶乘,而任意模数意义下的阶乘需要与当前答案贡献(也就是 \(ans_i\) )消去的质因子保持一致,预处理显然不现实,因此我们倒序枚举 \(i\) ,处理分子分母的同时累计相乘,也就同时求出了 \((n-i)!\)\(cnt_j!\)
      • 接下来就是数据结构优化一下,不再赘述。
  • 注意:

    • 要记得消除对 \(val\) 的影响,因为它不具有累积效应。
    • 每走一步,先累积 \(cnt\) ,再消除模数质因数。
  • 代码(非常丑陋):

#include<bits/stdc++.h>
#define int long long
const int N=10000010;
int n,p,x,y,maxn(0);
int son(1),mum(1),ans(0);
int cntson[N],cntmum[N];
int a[N],cnt[N],pmod[N],dat[N<<2];
int query(int pos,int l,int r,int ll,int rr){
  	if(!rr) return 0;
  	if(ll<=l&&r<=rr) return dat[pos];
  	int mid(l+r>>1),ans(0);
  	if(ll<=mid) ans+=query(pos<<1,l,mid,ll,rr);
  	if(mid<rr) ans+=query(pos<<1|1,mid+1,r,ll,rr);
  	return ans;
}
void modify(int pos,int l,int r,int x){
  	if(l==r) {dat[pos]++;return;}
  	int mid(l+r>>1);
  	if(x<=mid) modify(pos<<1,l,mid,x);
  	else modify(pos<<1|1,mid+1,r,x);
  	dat[pos]=dat[pos<<1]+dat[pos<<1|1];
}
int pow(int a,int k,int p){
  	int ans(1);
  	for(;k;a=a*a%p,k>>=1) if(k&1) ans=ans*a%p;
  	return ans;
}
void exgcd(int a,int b,int &x,int &y){
  	if(!b){x=1,y=0;return;}
  	exgcd(b,a%b,y,x),y=(y-(a/b)*x%p+p)%p;
}
void updateson(int x){
  	if(!x) return;
  	for(int i=1;i<=pmod[0];++i){
  		while(x%pmod[i]==0) {cntson[i]++,x/=pmod[i];}
  	}
  	son=son*x%p;
}
void updatemum(int x){
  	if(!x) return;
  	for(int i=1;i<=pmod[0];++i){
  		while(x%pmod[i]==0) {cntmum[i]++,x/=pmod[i];}
  	}
  	mum=mum*x%p;
}
int update(int x,int pos){
  	if(!x) return 0;
  	for(int i=1;i<=pmod[0];++i){
  		while(x%pmod[i]==0) cntson[i]+=pos,x/=pmod[i];
  	}
  	return x;
}
#undef int
int main(){
  	#define int long long
  	scanf("%lld%lld",&n,&p);
  	for(int i=1;i<=n;++i) scanf("%lld",&a[i]),maxn=std::max(maxn,a[i]);
  	int mod=p;
  	for(int i=2;i<=std::sqrt(mod);++i){
  		if(mod%i) continue;
  		pmod[++pmod[0]]=i;
  		while(!(mod%i)) mod/=i;
  	}
  	if(mod!=1) pmod[++pmod[0]]=mod;
  	for(int i=n;i>=1;--i){
  		int aa(1);
  		++cnt[a[i]];
  		updatemum(cnt[a[i]]);
  		updateson(n-i);
  		modify(1,1,maxn,a[i]);
  		int nn(query(1,1,maxn,1,a[i]-1)),tem;
  		tem=update(nn,1);
  		for(int j=1;j<=pmod[0];++j) 
  			aa=aa*pow(pmod[j],std::abs(cntmum[j]-cntson[j]),p)%p;
  		exgcd(mum,p,x,y);
  		x=(x%p+p)%p;
  		aa=aa*(tem*son%p)%p*x%p;
  		update(nn,-1);//消除影响
  		ans=(ans+aa)%p;
  	}
  	std::cout<<((ans+1)%p+p)%p;
}
posted @ 2023-06-24 20:57  Melting_Pot  阅读(16)  评论(0编辑  收藏  举报