PER-Permutation
Question
求一个有重复元素的排列的字典序
Solution
类似于康托展开
从后往前算出比每一位小的排列的个数
累加起来便是字典序,只不过这里的个数要考虑到重复元素
这里主要讲取模(因为这里模数不一定是质数)
推出式子后,发现每次需要乘 n−i n − i ,还要除以 cnt[a[i]] c n t [ a [ i ] ]
主要步骤是将模数分解为
m=Πki=1pni
m
=
Π
i
=
1
k
p
n
i
分
k
k
次求答案,记为(每次对
pni
p
n
i
取模),最后将答案合并成
Ans
A
n
s
相当于解下面这一组同余方程
Ans≡a1(mod
A
n
s
≡
a
1
(
m
o
d
pn1)
p
n
1
)
Ans≡a2(mod
A
n
s
≡
a
2
(
m
o
d
pn2)
p
n
2
)
⋮
⋮
Ans≡ak(mod
A
n
s
≡
a
k
(
m
o
d
pnk)
p
n
k
)
用扩欧或用中国剩余定理合并
要注意一种特殊情况
因为
pni
p
n
i
毕竟不是质数,它可能和分母有公因子,所以要先除掉分子分母所有的
p
p
<script type="math/tex" id="MathJax-Element-2569">p</script>(记录有多少)取完模在乘回来
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#define For(i,s,e) for(int i=(s); i<=(e); i++)
#define Rep(i,s,e) for(int i=(s); i>=(e); i--)
#define Lowbit(x) ((x)&(-x))
using namespace std;
typedef long long LL;
typedef pair<int, int> pii;
const int N = 300000 + 5;
int n, Mod, a[N], cnt[N], aMax;
LL T[N], Ans;
void Modify(int p){ while(p <= aMax + 1) T[p]++, p += Lowbit(p); }
int Query(int p){
if(!p) return 0;
LL Ans = 0;
while(p) Ans += T[p], p -= Lowbit(p);
return Ans;
}
int tot, Base[N], Pw[N], Mi[N];
void Divide(int x){
For(i, 2, x){
int _Pw = 1;
while(x && x % i == 0) x /= i, _Pw *= i;
if(_Pw != 1) Base[++tot] = i, Pw[tot] = _Pw;
}
}
struct NewNum{
int a, b;
void init(int x, int p){// x = a * p ^ b
a = x; b = 0;
while(a && a % p == 0) a /= p, b++;
}
}Trans;
void exGcd(int a, int b, LL & x, LL & y){
if(!b){ x = 1, y = 0; return; }
LL x1, y1;
exGcd(b, a % b, x1, y1);
x = y1, y = x1 - (a / b) * y1;
}
int Inv(int a, int p){ //a ^ -1
LL x, y;
exGcd(a, p, x, y);
return (x + p) % p;
}
int Calc(int p, int m){//-> % m
For(i, 1, max(aMax, n) + 1) T[i] = cnt[i] = 0;
int Ans = 1, F = 1; Mi[0] = 1; cnt[a[n]]++;
Modify(a[n]);
For(i, 1, max(aMax, n)) Mi[i] = Mi[i - 1] * p % m;
int num = 0;
Rep(i, n - 1, 1){
Trans.init(n - i, p);
num += Trans.b;
F = (1LL * F * Trans.a) % m;
Trans.init(++cnt[a[i]], p);
num -= Trans.b;
F = (1LL * F * Inv(Trans.a, m)) % m;
Modify(a[i]);
Ans = (Ans + 1LL * F * Query(a[i] - 1)% m * Mi[num] ) % m;
}
return Ans;
}
int main(){
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
cin>>n>>Mod;
Divide(Mod);
For(i, 1, n) scanf("%lld",&a[i]), aMax = max(aMax, a[i]);
Ans = Calc(Base[1], Pw[1]);
int M = Pw[1];
For(i, 2, tot){
/*中国剩余定理*/
/* int a = Calc(Base[i], Pw[i]);
int M = Mod / Pw[i];
Ans = (Ans + (LL)a * M % Mod * Inv(M, Pw[i]))%Mod;*/
/*扩欧*/
LL x, y;
exGcd(M, Pw[i], x, y);
x *= Calc(Base[i], Pw[i]) - Ans;
Ans += x * M;
M *= Pw[i];
Ans = (Ans % Mod + M) % M;
}
cout<<(Ans + Mod) % Mod<<endl;
return 0;
}