题解 P5367 【【模板】康托展开】
这是一道挺水的题
虽然我做了七次
好了好了,不废话了。
对于康托展开,先贴公式,方便对后面的代码进行理解:
就是
首先,暴力肯定行不通,肯定会 TLE ,因此,可以在康托展开的阶乘预处理时同时与处理好,他是第几大的。
接着,还有一个很严重的问题:用普通的数组肯定会炸,因此要用树状数组。
我解释的差不多了,剩下的一些小细节会在代码里注释出来:
#include<bits/stdc++.h>
using namespace std;
long long C[1000005],fact[1000005],n;
long long lowbit(long long x)
{
return x&(-x);
}
void add(long long x,long long y)
{
for(int i=x;i<=n;i+=lowbit(i))
C[i]+=y;
}
long long getsum(long long x)
{
int ans=0;
for(int i=x;i>0;i-=lowbit(i))
ans+=C[i];
return ans;
}
int main()
{
long long i,can=0;
cin>>n;
fact[0]=1;
for(i=1;i<=n;i++)
{
fact[i]=fact[i-1]*i%998244353;//方便康托展开
add(i,1);//预处理,存入树状数组C[i]中
}
for (i=1; i<=n; i++)
{
int x;
scanf("%d",&x);
can=(can+(getsum(x)-1)*fact[n-i])%998244353;//无时无刻模它,因为随时它都可能会超出long long的数据范围
add(x,-1);//由于这个数用过了,所以要减1
}
cout<<can+1<<endl;//记得加1,因为算出的是这个全排列前面还有几个全排列
return 0;
}
温馨提示:
没学好树状数组的话,
不推荐来做这题。
另外,感谢@Randolph、的思路