PER

PER-Permutation P3477

  • 题目大意:

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

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

    • 式子:

      • ans=i=1nvalai(ni)!jcolaicntj!

      • 式子中的 a 为原排列,vali 表小于 ai 的,且未访问过的数的个数,coli 为原排列中,从 in 的数的种类数,cnt 则是一个桶。
    • 转化:

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

    • 要记得消除对 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 @   Melting_Pot  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· .NET Core 中如何实现缓存的预热?
· 三行代码完成国际化适配,妙~啊~
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
点击右上角即可分享
微信分享提示