zoukankan      html  css  js  c++  java
  • CF1349D Slime and Biscuits 解题报告

    (n) 个人,第 (i) 个人有 (a_i)​ 个饼干,每次随机选择一个饼干,将其随机分配给除了它现在所有者的其他 (n-1) 个人,求使得一个人拥有所有饼干的期望步数,对 (998244353) 取模。

    (nle 10^5 , sum a_ile 3 imes 10^5)

    (E_i) 为所有饼干在第 (i) 个人手中的情况的期望时间那么总答案就是 (sumlimits_{i=1}^n E_i)

    显然这个东西并不好求,考虑加点东西转化一下,设 (D_i) 为所有饼干都在第 (i) 个人手中时才结束的期望时间, (P_i) 为所有饼干在第 (i) 个人手中的情况的概率,此时有 (sum P_i=1)

    设所有饼干从一个人全转移到另外一个人的期望时间是 (C) ,对于任意两人,这个 (C) 是一样的。

    那么可以列出关系式:

    [D_x=E_x+sumlimits_{i=1&i ot=x}^{n} (E_i+P_i imes C) ]

    也就是枚举第一次拿到所有饼干的人,再进行转移。

    考虑将 (i ot=x) 给消掉,将 (x=1,2,...,n) 的式子都加起来,可以得到:

    [sumlimits_{i=1}^n D_i=nsumlimits_{i=1}^n E_i + C imes (n-1)sumlimits_{i=1}^n P_i ]

    由于答案是 (sumlimits_{i=1}^n E_i) ,所以有 (Ans=sumlimits_{i=1}^n D_i - C(n-1))

    考虑如何求出 (D_x)(C) ,可以设一个 (f_x) 为目前有了 (x) 块饼干,收集到所有饼干的期望时间,显然有 (D_x=f_{a_x} , C=f_0)

    (0le x<sum) 时,有 (f_x=1+frac{sum-x}{sum}(frac{f_{x+1}}{n-1}+frac{(n-2)f_x}{n-1})+frac{xf_{x-1}}{sum}) , 显然有 (f_{sum}=0)

    上面的式子是判断选择的饼干,如果是他人的饼干,就有 (frac{1}{n-1}) 的概率给到自己, (frac{n-2}{n-1}) 的概率给别人,如果是自己的饼干,就一定会少掉一个。

    这个消元我不太懂,但是有一个神奇的思路,就是搞一个 (f_x) 的差分数组 (g_x=f_{x}-f_{x+1}) ,那么有 (f_x=sumlimits_{i=x}^{sum} g_i) ,其中 (g_{sum}=0)

    那原式可以写成 (f_{x+1} +g_x=1+frac{sum-x}{sum}(frac{f_{x+1}}{n-1}+frac{(n-2)(f_{x+1} +g_x)}{n-1})+frac{x(f_{x+1} +g_{x}+g_{x-1} )}{sum}) ,化简可得 (g_x=frac{sum(n-1)+x(n-1)g_{x-1}}{sum-x})

    那就可以 (mathcal{O(sum)}) 来递推出 (g_x)(f_x) 了。

    #include<bits/stdc++.h>
    using namespace std;
    
    #define int long long
    const int M=1e6+5,JYY=998244353;
    
    int read(){
    	int x=0,y=1;char ch=getchar();
    	while(ch<'0'||ch>'9') y=(ch=='-')?-1:1,ch=getchar();
    	while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
    	return x*y;
    }
    
    int n,sum=0,Ans=0,a[M],inv[M],F[M];
    void solve(){
    	n=read();
    	for(int i=1;i<=n;i++) sum+=a[i]=read();
    	inv[0]=inv[1]=1;
    	for(int i=2;i<=max(sum,n);i++) inv[i]=(JYY-JYY/i)*inv[JYY%i]%JYY;
    	F[0]=n-1;
    	for(int i=1;i<sum;i++) F[i]=(sum*(n-1)%JYY+i*(n-1)%JYY*F[i-1]%JYY)*inv[sum-i]%JYY;
    	for(int i=sum-1;i>=0;i--) F[i]=(F[i+1]+F[i])%JYY;
    	for(int i=1;i<=n;i++) Ans=(Ans+F[a[i]])%JYY;
    	Ans=(Ans-F[0]*(n-1)%JYY+JYY)%JYY;
    	printf("%lld
    ",Ans*inv[n]%JYY);
    }
    
    signed main(){
    	solve();
    }
    
  • 相关阅读:
    作用域和内存问题
    Javascript事件
    JavaScript学习总结(三)
    Javascript学习总结(二)
    JavaScript学习总结(一)
    vue开发搭建 1、 npm安装+vue脚手架安装 2、cnpm安装
    20181008
    RabbitMQ在.NetCore中的基础应用
    微软CRM 基于 ADFS自定义多重身份验证
    如何在ASP.NET Core中上传超大文件
  • 原文地址:https://www.cnblogs.com/wzp-blog/p/14269297.html
Copyright © 2011-2022 走看看