【模板】康托展开
题目描述
求 1 ∼ N 1\sim N 1∼N 的一个给定全排列在所有 1 ∼ N 1\sim N 1∼N 全排列中的排名。结果对 998244353 998244353 998244353 取模。
输入格式
第一行一个正整数 N N N。
第二行 N N N 个正整数,表示 1 ∼ N 1\sim N 1∼N 的一种全排列。
输出格式
一行一个非负整数,表示答案对 998244353 998244353 998244353 取模的值。
样例 #1
样例输入 #1
3
2 1 3
样例输出 #1
3
样例 #2
样例输入 #2
4
1 2 4 3
样例输出 #2
2
提示
对于 10 % 10\% 10%数据, 1 ≤ N ≤ 10 1\le N\le 10 1≤N≤10。
对于 50 % 50\% 50%数据, 1 ≤ N ≤ 5000 1\le N\le 5000 1≤N≤5000。
对于 100 % 100\% 100%数据, 1 ≤ N ≤ 1000000 1\le N\le 1000000 1≤N≤1000000。
Sol
考虑每一个位置上对答案的影响。
就拿样例当解释
2 1 3
对于第一个位置只有当1,在第一个位置上面的时候,才会对答案造成影响,而让1到第一个位置上面的时候,后面的数字就可以随便排列了,所有我们可以推出排名的式子了
A
n
s
=
∑
i
=
1
n
s
u
m
i
∗
(
n
−
i
)
!
Ans=\sum_{i=1}^{n} sum_i*(n-i)!
Ans=i=1∑nsumi∗(n−i)!
s
u
m
i
sum_i
sumi表示后缀比
a
i
a_i
ai小的数有几个,然后这个数提前剩下的数随便排就好了。
我们考虑怎么去维护这个东西,对于阶乘直接
O
(
n
)
O(n)
O(n)预处理出来就好了,对于
s
u
m
i
sum_i
sumi就可以轻松地打一个值域树状数组来维护,然后就a了
#include<bits/stdc++.h>
#define I inline
#define LL long long
#define inf 0x3f3f3f3f
#define RI register int
#define N 1000100
#define mod 998244353
#define debug puts("debug");
using namespace std;
I LL read()
{
LL res=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-f;ch=getchar();}
while(isdigit(ch))res=(res<<1)+(res<<3)+(ch&15),ch=getchar();
return res*f;
}
LL ans=0,n,a[N],sum[N],inv[N];
I int lowbit(int x){return x&(-x);}
I int add(int x,int k){for(;x<=N-100;x+=lowbit(x))sum[x]+=k;}
I int ask(int x){int Ans=0;for(;x;x-=lowbit(x))Ans+=sum[x];return Ans;}
I void init(int x){inv[1]=1;for(RI i=2;i<=x;i++)inv[i]=(inv[i-1]*i)%mod;}
int main()
{
n=read();init(n);for(RI i=1;i<=n;i++)a[i]=read();
for(RI i=n;i>=1;i--)ans=(ans+ask(a[i])*inv[n-i])%mod,add(a[i],1);
printf("%lld",ans+1);
return 0;
}