浅谈康托展开

最近在刷数论的模板题,康托展开可以说并不是特别难。

基本就是一个全排列,然后让你告诉他这是全排列里的第几个。

不妨拿出最一开始的序列看看 \(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.
posted @ 2021-12-18 18:59  Mercury_City  阅读(28)  评论(0编辑  收藏  举报