标签:小于 ch int 321 inline 展开 排列 康托
康托展开
公式
\[ ans=1+\sum_{i=1}^{n} A[i]\times(n-i)! \]
原理
如我想知道321是{1,2,3}中第几个小的数可以这样考虑 :
第一位是3,当第一位的数小于3时,那排列数小于321 如 123、 213 ,小于3的数有1、2 。所以有2× 2!个。再看小于第二位2的:小于2的数只有一个就是1 ,所以有1× 1!=1 所以小于321的{1,2,3}排列数有2×2!+1×1!=5个。所以321是第6个小的数。 2 ×2!+1× 1!+0× 0!就是康托展开。
再举个例子:1324是{1,2,3,4}排列数中第几个大的数:第一位是1小于1的数没有,是0个 0× 3! 第二位是3小于3的数有1和2,但1已经在第一位了,所以只有一个数2,1×2! 。第三位是2小于2的数是1,但1在第一位,所以有0个数 0×1! ,所以比1324小的排列有0×3!+1× 2!+0×1 !=2个,1324是第三个小数。(摘自百度)
用处
洛谷模板题是用于求某排列的排名。-->
传送门
此题要求:给出一个排列求他在全排列中的排名。 直接套式子。
打暴力的复杂度是 $ o(n^2) $,水不过去。
于是我们想到了用树状数组,具体写法还是看我以前写的一篇博客-->传送门
先预处理阶乘,用数组存,o(n),然后用类似于权值线段树的树状数组来节省时间来判重。
上代码
#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
template <typename T>
inline void read(T &x) {
x = 0;
int f = 1;
char ch = getchar();
while (!isdigit(ch)) {
if (ch == '-') f = -1;
ch = getchar();
}
while (isdigit(ch)) {
x = x * 10 + (ch ^ 48);
ch = getchar();
}
x *= f;
return;
}
template <typename T>
inline void write(T x){
if(x < 0) {
putchar('-');
x = -x;
}
if(x > 9)
write(x/10);
putchar(x % 10 + '0');
return;
}
//-------------------------以下是正文
int q[1000005],n;
long long ans,fac[1000005];
inline int lowbit(int x){return x&(-x);}
inline void update(int x,int y){
for(int i=x;i<=n;i+=lowbit(i))
q[i] += y;
}
inline int getsum(int a)
{
int ans=0;
for(int i=a;i;i-=lowbit(i))
ans+=q[i];
return ans;
}
int main(){
int x;
read(n);
fac[0]=1;
for(int i=1;i<=n;i++)
{
fac[i]=fac[i-1]*i%mod;
update(i,1);//一开始初始化
}
for(int i=1;i<=n;i++)
{
read(x);
ans=(ans+(getsum(x)-1)%mod*fac[n-i])%mod;
update(x,-1);//用一个×一个
}
write(ans+1);
cout<<endl;
}
还有逆康托展开什么的就懒的写了,反正就是把公式倒着递推嘛。相信各位大佬都是会的。
标签:小于,ch,int,321,inline,展开,排列,康托 来源: https://www.cnblogs.com/iloveori/p/12526492.html
本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享; 2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关; 3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关; 4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除; 5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。