康托展开
题目链接
P5367 【模板】康托展开
【模板】康托展开
题目描述
求 \(1\sim N\) 的一个给定全排列在所有 \(1\sim N\) 全排列中的排名。结果对 998244353
取模。
输入格式
第一行一个正整数 \(N\)。
第二行 \(N\) 个正整数,表示 \(1\sim N\) 的一种全排列。
输出格式
一行一个非负整数,表示答案对 998244353
取模的值。
输入 #1
3
2 1 3
输出 #1
3
输入 #2
4
1 2 4 3
输出 #2
2
说明/提示
对于\(10\%\)数据,\(1\le N\le 10\)。
对于\(50\%\)数据,\(1\le N\le 5000\)。
对于\(100\%\)数据,\(1\le N\le 1000000\)。
解题思路
公式:
\[ans=1+∑_{i=1}^nA[i]×(n−i)!
\]
其中\(A[i]\)代表\(\sum_{j=i}^{n}[a[j] < a[i]]\)
暴力解法:
时间复杂度:\(O(n^2)\)
树状数组优化:
时间复杂度:\(O(nlogn)\)
暴力代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10,mod=998244353;
int n,a[N];
int res=1;
bool v[N];
int main()
{
scanf("%lld",&n);
for(int i=0;i<n;i++)scanf("%lld",&a[i]);
for(int i=0;i<n;i++)
{
int cnt=0,fact=1;
for(int j=1;j<=n-i-1;j++)fact=(1ll*fact*j)%mod;
for(int j=1;j<a[i];j++)
if(!v[j])cnt=(1ll*cnt+fact)%mod;
v[a[i]]=true;
res=(1ll*res+cnt)%mod;
}
printf("%lld",res);
return 0;
}
优化代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e6+10,mod=998244353;
int n,a[N],fact[N];
int res=1;
int tr[N];
void add(int x,int y)
{
for(;x<=n;x+=x&-x)tr[x]+=y;
}
int ask(int x)
{
int res=0;
for(;x;x-=x&-x)res+=tr[x];
return res;
}
int main()
{
scanf("%d",&n);
for(int i=0;i<n;i++)scanf("%d",&a[i]),add(i+1,1);
fact[0]=1;
for(int i=1;i<N;i++)fact[i]=(1ll*fact[i-1]*i)%mod;
for(int i=0;i<n;i++)
{
res=(1ll*res+1ll*(ask(a[i])-1)*fact[n-i-1])%mod;
add(a[i],-1);
}
printf("%d",res);
return 0;
}
逆康托展开
题解链接
leetcode60. 排列序列
暴力时间复杂度:\(O(n^2)\)
暴力代码
class Solution {
public:
string getPermutation(int n, int k) {
//逆康托展开
string res;
vector<bool> v(10);
for(int i=0;i<n;i++)
{
int cnt=1;
for(int j=1;j<=n-i-1;j++)cnt*=j;
for(int j=1;j<=n;j++)
if(!v[j])
{
if(cnt<k)k-=cnt;
else
{
res+=to_string(j);
v[j]=true;
break;
}
}
}
return res;
}
};