1 条题解

  • 0
    @ 2025-8-24 21:49:23

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Sonnety
    如果说,生命的脚步终有一天被时间的尘埃掩埋,那么我们就永远不能停下脚步。||不支持互关

    搬运于2025-08-24 21:49:23,当前版本为作者最后更新于2023-10-02 08:05:57,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    可能更好的阅读体验

    (upd:对 Hack 的有关分析已更新,见上个人博客↑)

    解题思路:

    首先看到这道题求排名,感觉还挺像康托展开的,但是康拓展开求的是 不会出现重复元素的排列 的排名,而这道题的难题主要有两个:

    可重复和未知模数。

    对于可能重复的元素:

    我们思考康托展开本身的公式。

    对于一个 11nn 的排列:a1,a2,a3,,an{a_1,a_2,a_3,\dots,a_n},其排名为:

    i=1nsumai(ni)!\sum_{i=1}^{n} sum_{a_i}\cdot (n-i)!

    sumaisum_{a_i} 表示在 aia_i 之后比它元素小的个数。

    int ans=0;
    for(int i=1;i<=n;i++){
        int sum=0;
       	for(int j=i;j<=n;j++)
    	if(a[i]<a[j])sum++;//统计sum
        ans=(ans+sum*jc[n-i])%998244353;//计算ans                       
    }
    printf("%d",ans+1);//别忘了+1
    
    

    那考虑如何去重呢?

    我们的 (ni)!(n-i)! 表示的是 (i+1)n(i+1)-n 位置所有的方案数,对于不重复元素,两个元素互换位置是不同的方案,而重复的元素,两个元素互换位置方案相同,造成了方案数的重复计算。

    因此,我们考虑将重复元素提出来,例如 39,39,39,39{39,39,39,39},它是一个方案,却计了 4!4! 次。

    所以,设从 ini-n 位置一共有 pieceipiece_i 种元素,cntjcnt_{j} 表示在当前的 pieceipiece_i 下的第 jj 个元素,答案为:

    $$ans=\sum_{i=1}^{n} \dfrac{sum_{a_i}\cdot (n-i)!}{\prod_{j=1}^{piece_{i}} cnt_j} $$

    然后你发现,如果从左往右扫,不断删除元素对于 cntjcnt_j 的处理较难,所以考虑从右往左倒序枚举,这样就成为了加入元素,无论是分子还是分母都较为简单。

    对于不明的模数

    对于不定模数,或许有两种解决方式:

    • 使用不依赖模数性质的算法或利用给出的模数自带的性质。

    • 对模数进行特殊的处理。

    不知道数据有没有 114514 一类的模数,但是显然 114514 不是质数,所以费马小定理不能使用。

    而我们只能考虑扩展欧几里得算法了,它要求我们求解的模数与求解数互质。

    所以我们可以对分母 j=1pieceicntj\prod_{j=1}^{piece_{i}} cnt_j 和模数分解质因数,对于模数没有的质因子,扩展欧几里得求逆元。

    对于共同的质因子,我们考虑直接消去,因为 ansans 是整数,所以 $\dfrac{sum_{a_i}\cdot (n-i)!}{\prod_{j=1}^{piece_{i}}\limits cnt_j} cnt_j$ 是整数,所以若存在共同质因子 pip_i,其分子一定存在比分母更多的质因子 pip_i,所以直接消去分子和分母所有的 pip_i,然后使分子乘上本身比分母多的 pip_i 即可。

    (tips:有关Hack,请注意这里的分子包含 sumasuma,即帖中的 ww。)

    使用树状数组优化一下,跑的还是比较快的。

    #include<bits/stdc++.h>
    #define il inline
    #define rg register int
    #define cout std::cout
    #define cerr std::cerr
    #define endl '\n'
    #define int long long
    typedef long long ll;
    typedef unsigned long long ull;
    typedef double ff;
    typedef long double llf;
    const ff eps=1e-8;
    int Max(int x,int y)    <% return x<y?y:x; %>
    int Min(int x,int y)    <% return x<y?x:y; %>
    int Abs(int x)  <% return x>0?x:-x; %>
    #if ONLINE_JUDGE
    char INPUT[1<<20],*p1=INPUT,*p2=INPUT;
    #define getchar() (p1==p2 && (p2=(p1=INPUT)+fread(INPUT,1,1<<20,stdin),p1==p2)?EOF:*p1++)
    #endif
    il int read(){
        char c=getchar();
        int x=0,f=1;
        while(c<48) <% if(c=='-')f=-1;c=getchar(); %>
        while(c>47) x=(x*10)+(c^48),c=getchar();
        return x*f;
    }const int maxn=3e5+5;
    
    int n,Mod,ptot,p[maxn],a[maxn],sa[maxn],rank[maxn],len;
    int cnt_numerator[maxn],cnt_denominator[maxn];
    ll sum[maxn],ans,cnt[maxn];
    
    il ll mymod(ll x){ return x<Mod?x:x-Mod; }
    
    il ll qpow(ll x,int k){
    // x^k
        ll res=1;
        while(k){
            if(k&1) res=res*x%Mod;
            x=x*x%Mod;
            k=k>>1;
        }
        return res;
    }
    
    il void exgcd(int a,int b,int &x,int &y){
    // 求得x是a在模Mod意义下的乘法逆元
        if(!b)  return x=1,y=0,void();
        exgcd(b,a%b,y,x);y=(y-(a/b)*x%Mod+Mod)%Mod;
    }
    
    #define lowbit(x) (x&-x)
    il ll query(int pos){
        ll res=0;
        while(pos){
            res+=sum[pos];
            pos-=lowbit(pos);
        }
        return res;
    }
    il void update(int pos,int val){
        while(pos<=len){
            sum[pos]+=val;
            pos+=lowbit(pos);
        }
    }
    #undef lowbit
    
    il ll solve_numerator(ll numerator){
    // 对分子分解质因数
        if(!numerator)  return 1;
        for(rg i=1;i<=ptot;++i){
            if(numerator%p[i])  continue;
            while(!(numerator%p[i]))  ++cnt_numerator[i],numerator/=p[i];
        }
        return numerator;
    }
    
    il void back(ll numerator){
    // 撤回ssum对cnt_numerator的影响
        if(!numerator)  return;
        for(rg i=1;i<=ptot;++i){
            if(numerator%p[i])  continue;
            while(!(numerator%p[i]))  --cnt_numerator[i],numerator/=p[i];
        }
    }
    
    il ll solve_denominator(ll denominator){
    // 对分母分解质因数
        if(!denominator)    return 1;
        for(rg i=1;i<=ptot;++i){
            if(denominator%p[i])    continue;
            while(!(denominator%p[i]))    ++cnt_denominator[i],denominator/=p[i];
        }
        return denominator;
    }
    
    il void work(){
        int numerator=1,denominator=1,res=1,x=0,y=0,save;
        for(rg i=n;i>=1;--i){
            res=1;
            ++cnt[a[i]];
            numerator=numerator*solve_numerator(n-i)%Mod;
            save=numerator;
            denominator=denominator*solve_denominator(cnt[a[i]])%Mod;
            update(rank[i],1);
            int ssum=query(rank[i]-1);
            if(!ssum)   continue;   // 没有比当前位小的直接continue
            numerator=numerator*solve_numerator(ssum)%Mod;
            exgcd(denominator,Mod,x,y);     // 求非共同质因子的逆元x
            x=mymod(x%Mod+Mod);
            for(rg j=1;j<=ptot;++j) res=res*qpow(p[j],cnt_numerator[j]-cnt_denominator[j])%Mod; // 消去共同质因子
            res=res*numerator%Mod*x%Mod;
            ans=mymod(ans+res);
            back(ssum);numerator=save;
        }
    }
    
    il void input(){
        n=read(),Mod=read();
        for(rg i=1;i<=n;++i)    a[i]=read(),sa[i]=rank[i]=a[i];
        std::sort(sa+1,sa+1+n);
        len=std::unique(sa+1,sa+1+n)-(sa+1);
        for(rg i=1;i<=n;++i)    rank[i]=std::lower_bound(sa+1,sa+1+len,rank[i])-sa; // 离散化
        int save=Mod;
        for(rg i=2;i<=sqrt(Mod);++i){
            if(Mod%i)   continue;
            p[++ptot]=i;
            while(!(Mod%i))   Mod/=i;
        }
        if(Mod^1)   p[++ptot]=Mod;
        Mod=save;
    }
    
    signed main(){
    #ifndef ONLINE_JUDGE
    freopen("PER.in","r",stdin);
    #endif
        input();
        work();
        printf("%lld\n",mymod(ans+1));
        return 0;
    }
    
    • 1

    信息

    ID
    2554
    时间
    2000ms
    内存
    125MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者