康托展开学习笔记

【模板】康托展开

题目描述

1 ∼ N 1\sim N 1N 的一个给定全排列在所有 1 ∼ N 1\sim N 1N 全排列中的排名。结果对 998244353 998244353 998244353 取模。

输入格式

第一行一个正整数 N N N

第二行 N N N 个正整数,表示 1 ∼ N 1\sim N 1N 的一种全排列。

输出格式

一行一个非负整数,表示答案对 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 1N10

对于 50 % 50\% 50%数据, 1 ≤ N ≤ 5000 1\le N\le 5000 1N5000

对于 100 % 100\% 100%数据, 1 ≤ N ≤ 1000000 1\le N\le 1000000 1N1000000

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=1nsumi(ni)!
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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值