浅谈康托展开
最近在刷数论的模板题,康托展开可以说并不是特别难。
基本就是一个全排列,然后让你告诉他这是全排列里的第几个。
不妨拿出最一开始的序列看看 \(1,2,3,4,... n\)
这个时候,我假如说有一个混乱序列
\(a_1,a_2,a_3,...,a_n \quad \quad \quad \forall a_i \in[1,n] \\ \qquad \qquad \qquad \qquad \qquad \forall a_i \not= \forall a_j (j \in [1,n],i\not= j)\)
那么,你想,如果我的第一个是 \(1\) ,是不是证明我的这个全排列至少第一位没有变动,感性的感觉就是大致整个序列变动都不大。雀食,那如果我的第一个是 \(2\) 呢,是不是证明后面都轮回一遍了,那么对于答案的贡献也很显然就是 \((n-1)!\)
那么假如说我现在看到了第 \(i\) 位,那我怎么知道我现在应该加上几倍的 \((n-i)!\) 呢,那就是看应该有几个数排在我前面,可是没有,转化问题,就是看后面有几个数小于我,这些数按道理都该在前面的。所以,差不多转化成了一种逆序对一样的东东。
逆序对直接上树状数组。
#include <bits/stdc++.h>
#define debug puts("I ak IOI several times");
#define pb push_back
#define int long long
using namespace std;
template <typename T>inline void read(T& t){
t=0; register char ch=getchar(); register int fflag=1;
while(!('0'<=ch&&ch<='9')){if(ch=='-') fflag=-1;ch=getchar();}
while(('0'<=ch&&ch<='9')){t=((t<<1)+(t<<3))+ch-'0'; ch=getchar();} t*=fflag;
}
template <typename T,typename... Args> inline void read(T& t, Args&... args){
read(t);read(args...);
}
template <typename T>inline void write(T x){
if(x<0) putchar('-'),x=~(x-1); int s[40],top=0;
while(x) s[++top]=x%10,x/=10; if(!top) s[++top]=0;
while(top) putchar(s[top--]+'0');
}
const int mod=998244353,MAXN=1000005;
int n,ans;
int a[MAXN],p[MAXN];
int sum[4*MAXN];
void intt(){
read(n);
for(int i=1;i<=n;++i) read(a[i]);
p[1]=1;
for(int i=2;i<=MAXN;++i) p[i]=(1ll*p[i-1]*i)%mod;
return;
}
int lowbit(int x){return x&-x;}
void update(int x,int s){
while(x<=n){
sum[x]+=s; sum[x]%=mod;
x+=lowbit(x);
}
}
int query(int x){
int res=0;
while(x){
res+=sum[x];res%=mod;
x-=lowbit(x);
}
return res;
}
signed main(){
intt();
for(int i=n;i>=1;--i){
int s=query(a[i]);
update(a[i],1);
ans+=p[n-i]*s; ans%=mod;
}
cout<<ans+1<<endl;
return 0;
}
//Welcome back,Chtholly.