2020牛客暑期多校训练营(第六场)A African Sort

African Sort

https://ac.nowcoder.com/acm/contest/5671/A

A African Sort
题意: 给定排列 p,每次可以选一个下标集合等概率打乱包含的数并花费集合大小的代价,求给 p 排升序最优策略下最小代价的期望,对 998244353 取模

做法:一个permutation可以看成若干个环(i连p[i]),显然不存在大小不为1的环即为排序完成。那么对所有大小不为1的环进行随机排序操作,记对大小为n的环进行操作至全部有序的期望花费为f(n)。

对于大小为n的环,随机打乱一共有n!种情况。可以发现,指定i个位置组成一个环,有C(n,i)*(i-1)!*(n-i)!种情况。C(n,i)为指定i个位置的方案数,(i-1)!为指定的i个位置能组成多少种不同的环,即圆排列,(n-i)!为其余n-i个位置任意排列。
所以枚举环的大小i可以得到表达式

化简得到一个带系数的前缀表达式,这里其实也可以维护前缀直接开始做了

但是可以也可以想办法变成逐项递推式,我们有f(n-1)的表达式

加起来

化简得到主递推式

再由

可得到递推边界

到这里就做完了,关键是求这个f函数,贴个代码。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const ll mod = 998244353;

ll mypow(ll a,ll b){
    ll ret=1;
    while(b){
        if(b&1)ret=ret*a%mod;
        a=a*a%mod;b>>=1;
    }
    return ret;
}

int a[100010],vis[100010];
ll f[100010];
int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    f[2]=4;
    for(int i=3;i<100010;i++){
        f[i]=(f[i-1]+1)*i%mod*mypow(i-1,mod-2)%mod;
    }
    int n,m;cin>>n>>m;
    for(int i=1;i<=m;i++){
        for(int i=1;i<=n;i++){
            cin>>a[i];
            vis[i]=0;
        }
        ll ans=0;
        for(int i=1;i<=n;i++){
            if(vis[i])continue;
            vis[i]=1;
            int now=a[i];
            int cnt=1;
            while(now!=i){
                vis[now]=1;
                now=a[now];
                cnt++;
            }
            ans+=f[cnt]%mod;
        }
        cout<<ans%mod<<endl;
    }
}
全部评论
请问怎么证明 每次操作整个环 一定比 先操作环的一部分再操作另一部分 更优啊
点赞 回复 分享
发布于 2020-09-04 15:37
(n-1)!就是圆排列
点赞 回复 分享
发布于 2020-07-27 22:51
如果这个环随机打乱后还要是个一个环,则为(n-1)!,如果打乱后不要求是一个环是n!
点赞 回复 分享
发布于 2020-07-27 22:50
哦大概清楚了,是先把环变成链,再找一些点连成环。
点赞 回复 分享
发布于 2020-07-27 22:49
大小为 n 的环随机打乱为什么是 n! 种不是 (n-1)! 种啊
点赞 回复 分享
发布于 2020-07-27 22:43

相关推荐

野猪不是猪🐗:我assume that你must技术aspect是solid的,temperament也挺good的,however面试不太serious,generally会feel style上不够sharp
点赞 评论 收藏
分享
评论
5
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务