PER-Permutation P3477
-
题目大意:
一个元素个数为 n 的多重集的一个排列和 m ,求这个排列的字典序排名取模 m 。
-
思考:
- 首先,我们显然能够根据题意想到 康托展开,但它们之间当然是有区别的(
这不是废话么) , 本题的排列是一个可重的,范围不定的,不连续的无序排列。
- 知道了区别,下一步呢?我们仍可以依照康托展开的思想,对排列的每一位进行考虑。
- 本题的任意模数并不十分友好,因为在这种意义下逆元并不存在,因此考虑将模数分解,对每一个互质于当前答案贡献的模数分别计算,最后合并即可。
-
实现:
-
式子:
-
转化:
- 因为模数的不确定性,导致逆元无法求,因此我们先将模数 p 分解为质因数 pi ,再把每一个 ansi 中存在的 p 的因数消去,这样也就保证了 ansi 与 p 互质,逆元也就十分好求了。
- ansi 是分数的形式,因此我们要对分子分母分别消去 p 的质因子,最后我们当然要把消去的质因子再乘回来,而 ansi 一定是个整数,也就是说,对于一个 pi ,分子中的数量一定比分母中的大,那么消去的(或者说需要乘回来的)质因子的形式也就是 pik(k∈Z) 。
- 我们需要阶乘,而任意模数意义下的阶乘需要与当前答案贡献(也就是 ansi )消去的质因子保持一致,预处理显然不现实,因此我们倒序枚举 i ,处理分子分母的同时累计相乘,也就同时求出了 (n−i)! 与 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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· .NET Core 中如何实现缓存的预热?
· 三行代码完成国际化适配,妙~啊~
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?