HDU-6825 Set1【2020 Multi-University Training Contest 5】【组合数学】

题目

HDU-6825

题意

值为1-n的n个数的集合(n为奇数),每次进行一次操作:删除集合中最小的数,同时随机删除集合中的另一个数,进行(n-1)/2次这样的操作,最终剩下一个数,现在问集合1-n每个数最终被留下的概率

题解

我们定义操作中选择最小的数删除为操作1,随机删除的为操作2;
我们考虑每个i最终留下的方案,首先当i前面的数大于等于后边的数时才有可能会被留下来,即i-1>=n-i;因为如果前边的数小于后边的数时,当i前边的数被操作1删除时,集合中的数量还大于1,而i是最小的数,会在下一次操作中被操作1选择删除掉;
我们设置Li-1Rn-i
对于i-1>=n-iL>=R时,计算留下的方案,当i被最终被留下,那么对于R中的数,一定是当操作1删除L的数时,被操作2一对一地删除掉的(因为如果R是被操作1删除掉的话,那么i肯定已经被删除掉了),那么这部分的被选择的方案为:
在这里插入图片描述
这里C为在L里选R个数,A为对随机选择的R进行全排列;

剩下的L-R个数,也就是在L中剩下的数是一半是被操作1删除,一半是被操作2删除的,那么这部分被选择的方案为:
(设置m = ((i-1)-(n-i)) = 2*i-n-1
在这里插入图片描述
将两部分的选择数相乘就是i最终被留下方案数,设cnt[i]i被留下的方案数,公式如下:
在这里插入图片描述
化简后如下:
在这里插入图片描述
将每数的cnt累加起来就是总的方案数,即:
在这里插入图片描述
然后每个数的概率就是cnt[i]/sum
直接跑的话会t,我们先把要用到的预处理一下

代码

/*
 * @author: arc
 * @date: 2020-08-08 20:52:01
 */
#include
#include
typedef long long ll,LL;
#define mod 998244353
ll powmod(ll a,ll b) {ll res=1;a%=mod; assert(b>=0); for(;b;b>>=1){if(b&1)res=res*a%mod;a=a*a%mod;}return res;}
const int maxn = 5e6+10;

ll cnt[maxn], A[maxn], invpow2[maxn], invA[maxn];

ll C(ll n, ll m){
	return (ll) A[n] * invA[m] % mod * invA [n-m] %mod;
}

void init(){
    ll n = maxn;
    A[0] = 1;
    invA[0] = 1;
    invpow2[0] = 1;
    ll ink2 = powmod(2, mod - 2);
    for (ll i = 1; i <= n; i++){
        A[i] = A[i - 1] * i % mod;
        invA[i] = invA[i-1] * powmod(i, mod - 2) % mod;
        invpow2[i] = invpow2[i - 1] * ink2 % mod;
    }
}

ll cal(ll i, ll n){
    ll num = C(i - 1, n - i) * A[n - i] % mod * A[2 * i - n - 1] % mod;
    num = num * invpow2[(2*i-n-1)/2] % mod * invA[(2*i-n-1)/2] % mod;
    return num;
}

int main(){
    init();
    int cas;
    scanf("%d", &cas);
    while(cas--){
        int n;
        scanf("%d", &n);
        ll sum = 0;
        for (int i = 1; i <= n; i++){
            if(i - 1 >= n - i){
                cnt[i] = cal(i, n);
                sum += cnt[i];
                sum %= mod;
            } else {
                cnt[i] = 0;
            }
        }
        ll ans;
        sum = powmod(sum, mod - 2);
        for (int i = 1; i <= n;i++){
            ans = cnt[i] * sum % mod;
            if(i == n)
                printf("%lld\n", ans);
            else
                printf("%lld ", ans);
        }
    }
    return 0;
}

你可能感兴趣的:(笔记)