【题解】P7322 「PMOI-4」排列变换

P7322 「PMOI-4」排列变换

题意

给定常数 \(k\)。对于一个长度为 \(n\)排列 \(a\),定义

\[f(a)=\{\max_{1 \le i \le k} \{a_i\},\max_{2 \le i \le k+1} \{a_i\},\cdots,\max_{n-k+1 \le i \le n} \{a_i\}\} \]

对于一个长度为 \(n\)序列 \(a\),定义其权值 \(w(a)\)\(a\) 中不同的数的个数。

现在,\(\text{ducati}\) 想知道,对于所有长度为 \(n\) 的排列 \(p\),它们的 \(w(f(p))\) 之和。

题解

知识点:组合数。

启发:

  • 一种贡献转化方式。

很好的题目,提供一种简单做法。

直接做是困难的,先思考 \(f(a)\) 的本质。

可以发现 \(f(a)\) 相当于模拟一个长度为 \(k\) 的滑动窗口滑过排列 \(a\),并依次记录每个位置下窗口内的最大值。

\(w(f(a))\) 也就是最大值的突变次数再加上 \(1\)

接着转变角度,考虑 \(1\sim n\) 每个数会在多少个排列中引起突变。

假设这个数为 \(i\),那么无非就两种情况会引起突变:

  • 窗口刚移入 \(i\)\(i\) 变成最大值。

  • 窗口刚移出 \(i\)\(i\) 不再是最大值。

不过这还不够,仔细思考,发现这样设计的话,贡献会重复,考虑加紧限制:

  • 窗口刚移入 \(i\),最大值增大\(i\)

  • 窗口刚移出 \(i\),最大值不再为 \(i\),且最大值变小

仔细分析,发现这样设计是不重不漏的,下一步考虑计算。

观察到,第二种情况和第一种本质是相同的,无非就是反过来了,那我们直接用第一种情况的排列数乘以二就可以得到总贡献。

首先,有 \((n-k)\) 个位置能让 \(i\) 处于窗口最左端且窗口还能继续滑动。

其次,要让窗口移出后最大值变小,则 \(i\) 后面的 \(k\) 个数都得比 \(i\) 小,也就是从 \((i-1)\) 个数中选 \(k\) 个组成排列。

其他的数就无所谓了,任意排列均可。

结合组合数,可以推出如下式子:

\[\large 2(n-k)\times \binom{i-1}{k} \times (n-k-1)! \times k! \]

化简一下得到:

\[\large 2(n-k)!\times k! \times \binom{i-1}{k} \]

则最终答案就是:

\[\displaystyle n!+\sum_{i=1}^n\large 2(n-k)!\times k! \times \binom{i-1}{k} \]

#include<bits/stdc++.h>
using namespace std;

#define rep(i,l,r) for(int i=(l);i<=(r);++i)
#define per(i,l,r) for(int i=(r);i>=(l);--i)
#define pr pair<int,int>
#define fi first
#define se second
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define sz(x) (int)(x).size()
#define bg(x) (x).begin()
#define ed(x) (x).end()

#define N 502504
#define int long long

const int mod=998244353;

namespace comb{
    int fac[N];

    inline int inv(int a){
        int b=mod-2,ans=1;

        while(b){
            if(b&1){
                ans=ans*a%mod;
            }
            a=a*a%mod;
            b>>=1;
        }

        return ans;
    }

    inline int C(int n,int m){
        if(n<m){
            return 0;
        }

        return fac[n]*inv(fac[m])%mod*inv(fac[n-m])%mod;
    }

    inline void init(int lim){
        fac[0]=1;

        rep(i,1,lim){
            fac[i]=fac[i-1]*i%mod;
        }
    }
};

using comb::C,comb::fac;

int n,k;

signed main(){
    // freopen(".in","r",stdin);
    // freopen(".out","w",stdout);
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);

    comb::init(5e5);

    cin>>n>>k;

    int ans=fac[n];

    rep(i,1,n){
        ans=(ans+2*fac[n-k]%mod*fac[k]%mod*C(i-1,k)%mod)%mod;
    }

    cout<<ans;

    return 0;
}
posted @ 2025-05-03 09:38  Lucyna_Kushinada  阅读(10)  评论(0)    收藏  举报