← 回到所有文章

CF850F Rainbow Balls 题解

题意 有一袋 n 个颜色球,第 i 个颜色的球有 ai 个。 当袋子里至少有两个不同颜色的球时,执行以下步骤: 1. 一个接一个的按照顺序随机取出两个的球,这些球的颜色可能是一样的。 2. 把第二个球…

题解文章的简约插画封面

题意

有一袋 nn 个颜色球,第 ii 个颜色的球有 aia_i 个。

当袋子里至少有两个不同颜色的球时,执行以下步骤:

  1. 一个接一个的按照顺序随机取出两个的球,这些球的颜色可能是一样的。
  2. 把第二个球涂成第一个球的颜色,然后把两个球放回袋子里。

求期望操作次数。

n≤2500,1≤ai≤105n\le 2500,1\le a_i \le 10^5

题解

考虑计算最后颜色全是 xx 的情况,设 fif_i 表示已经有 ii 个 xx 颜色,把这所有都变成 xx 还需要的期望次数。先计算一下选出两个求,一个是 xx,另一个不是的概率设为 pp,并且记 ss 表示球总数:

p=i(s−i)s(s−1)p=\frac{i(s-i)}{s(s-1)}

然后列出转移方程,先考虑边界,fs=0f_s=0,f0f_0 不存在,因为永远走不到。按照正常的套路是:

fi=pfi−1+pfi+1+(1−2p)fi+1f_i=pf_{i-1}+pf_{i+1}+(1-2p)f_i+1

即表示走出去这一步,但是发现这样干会错,应为走出去这一步不一定就能走到 fnf_n 了,可能直接走到 f0f_0,所以这一步不是所有情况都能贡献到。假设贡献为 vv,可以知道 vv 就是能走到 fnf_n 的概率。dp 计算一下,设 gig_i 表示有 ii 个为 xx 的球,到最后的概率所有球都变成 xx 的概率:

g0=0,gs=1gi=pgi−1+pgi+1+(1−2p)gigi+1−gi=gi−gi−1\begin{align} &g_0=0,g_s=1\\ &g_i=pg_{i-1}+pg_{i+1}+(1-2p)g_i\\ &g_{i+1}-g_{i}=g_i-g_{i-1} \end{align}

所以 gg 是等差数列,并且知道首项和尾项,可以解出:

v=gi=isv=g_i=\frac{i}{s}

带入列一下转移方程:

fi=pfi−1+pfi+1+(1−2p)fi+isfi−fi+1=fi−1−fi+s−1s−ifi+1=2fi−fi−1−s−1s−i\begin{align} &f_i=pf_{i-1}+pf_{i+1}+(1-2p)f_i+\frac{i}{s}\\ &f_{i}-f_{i+1}=f_{i-1}-f_i+\frac{s-1}{s-i}\\ &f_{i+1}=2f_i-f_{i-1}-\frac{s-1}{s-i} \end{align}

由于没有 f0f_0,可得 f2=2f1−1f_2=2f_1-1,现在需要把 f1f_1 解出来。根据 fi−fi+1=fi−1−fi+s−1s−if_{i}-f_{i+1}=f_{i-1}-f_i+\frac{s-1}{s-i} 可以把 f1f_1 和 fsf_s 用差分的方式列出来:

f1−0=f1−fs=∑i=2sfi−fi−1f_1-0=f_1-f_s=\sum_{i=2}^s f_i-f_{i-1}

考虑差分数列上相邻两项的差的贡献:

∑i=2sfi−fi−1=(s−1)(f1−f2)+∑i=2s−1s−1s−i(s−i)=(s−1)(f1−f2)+(s−2)(s−1)\begin{align} &\sum_{i=2}^s f_i-f_{i-1}\\ =&(s-1)(f_1-f_2)+\sum_{i=2}^{s-1} \frac{s-1}{s-i}(s-i)\\ =&(s-1)(f_1-f_2)+(s-2)(s-1) \end{align}

把 f2=2f1−1f_2=2f_1-1 带入得 f1=(s−1)2sf_1=\frac{(s-1)^2}{s}。答案就是 ∑i=1nfai\sum_{i=1}^n f_{a_i}

#include<bits/stdc++.h>
// #define ONLINE_JUDGE
#define INPUT_DATA_TYPE int
#define OUTPUT_DATA_TYPE int
inline __attribute((always_inline)) INPUT_DATA_TYPE read(){register INPUT_DATA_TYPE x=0;register char f=0,c=getchar();while(c<'0'||'9'<c)f=(c=='-'),c=getchar();while('0'<=c&&c<='9')x=(x<<3)+(x<<1)+(c&15),c=getchar();return f?-x:x;}void print(OUTPUT_DATA_TYPE x){if(x<0)x=-x,putchar('-');if(x>9)print(x/10);putchar(x%10^48);return;}

#define MODINT_TYPE long long
#define barK (64)

namespace MODINT{
	unsigned long long d;
    __uint128_t m;
    inline __attribute((always_inline)) void init(const long long &mod){
        m=(((__uint128_t)1)<<barK)/(d=mod);
        return;
    }

    inline __attribute((always_inline)) unsigned long long mod(const unsigned long long &x){
        register unsigned long long w=(m*x)>>barK;
        w=x-w*d;
        return w>=d?w-d:w;
    }

    MODINT_TYPE exgcd(MODINT_TYPE a,const MODINT_TYPE b,MODINT_TYPE &x,MODINT_TYPE &y){
        if(!b){
            x=1;
            y=0;
            return a;
        }
        MODINT_TYPE d=exgcd(b,a%b,y,x);
        y-=a/b*x;
        return d;
    }

    inline __attribute((always_inline)) MODINT_TYPE inv(const MODINT_TYPE &n,const MODINT_TYPE &p){
        MODINT_TYPE x,y;
        exgcd(n,p,x,y);
        x%=p;
        return x>=0?x:x+p;
    }

    struct MODNUM{
        MODINT_TYPE val;
        inline __attribute((always_inline)) MODNUM(const MODINT_TYPE &x){
            if(x<0){
                val=d-mod(-x);
                if(val>=d) val-=d;
            }else val=mod(x);
            return;
        }
        inline __attribute((always_inline)) MODNUM(){val=0;}
        inline __attribute((always_inline)) MODNUM operator + (const MODNUM& o) const{return (MODNUM){(val+o.val>=d)?(val+o.val-d):(val+o.val)};}
        inline __attribute((always_inline)) MODNUM operator + (const MODINT_TYPE& o) const{return *this+MODNUM(o);}
        friend inline __attribute((always_inline)) MODNUM operator + (const MODINT_TYPE &o,const MODNUM&a){return a+o;}
        inline __attribute((always_inline)) MODNUM operator - (const MODNUM& o) const{return (MODNUM){(val-o.val<0)?(val-o.val+d):(val-o.val)};}
        inline __attribute((always_inline)) MODNUM operator - (const MODINT_TYPE& o) const{return *this-MODNUM(o);}
        friend inline __attribute((always_inline)) MODNUM operator - (const MODINT_TYPE &o,const MODNUM&a){return MODNUM(o)-a;}
        inline __attribute((always_inline)) MODNUM operator * (const MODNUM& o) const{return (MODNUM){mod(val*o.val)};}
        inline __attribute((always_inline)) MODNUM operator * (const MODINT_TYPE& o) const{return *this*MODNUM(o);}
        friend inline __attribute((always_inline)) MODNUM operator * (const MODINT_TYPE &o,const MODNUM&a){return a*o;}
        inline __attribute((always_inline)) MODNUM operator / (const MODNUM& o) const{return (MODNUM){mod(val*inv(o.val,d))};}
        inline __attribute((always_inline)) MODNUM operator / (const MODINT_TYPE& o) const{return *this/MODNUM(o);}
        friend inline __attribute((always_inline)) MODNUM operator / (const MODINT_TYPE &o,const MODNUM&a){return MODNUM(o)/a;}

        inline __attribute((always_inline)) MODNUM operator ++(){
            ++val;
            if(val>=d) val-=d;
            return *this;
        }
        inline __attribute((always_inline)) MODNUM operator ++(const int){
            MODNUM tmp=*this;
            ++val;
            if(val>=d) val-=d;
            return tmp;
        }
        inline __attribute((always_inline)) MODNUM operator --(){
            --val;
            if(val<0) val+=d;
            return *this;
        }
        inline __attribute((always_inline)) MODNUM operator --(const int){
            MODNUM tmp=*this;
            --val;
            if(val<0) val+=d;
            return tmp;
        }

        inline __attribute((always_inline)) MODNUM& operator += (const MODNUM& o) {return *this=*this+o;}
        inline __attribute((always_inline)) MODNUM& operator += (const MODINT_TYPE& o) {return *this=*this+o;}
        inline __attribute((always_inline)) MODNUM& operator -= (const MODNUM& o) {return *this=*this-o;}
        inline __attribute((always_inline)) MODNUM& operator -= (const MODINT_TYPE& o) {return *this=*this-o;}
        inline __attribute((always_inline)) MODNUM& operator *= (const MODNUM& o) {return *this=*this*o;}
        inline __attribute((always_inline)) MODNUM& operator *= (const MODINT_TYPE& o) {return *this=*this*o;}
        inline __attribute((always_inline)) MODNUM& operator /= (const MODNUM& o) {return *this=*this/o;}
        inline __attribute((always_inline)) MODNUM& operator /= (const MODINT_TYPE& o) {return *this=*this/o;}

        inline __attribute((always_inline)) operator MODINT_TYPE(){return val;}
    };
}

#define mint MODINT::MODNUM

int arr[2510];
mint dp[100010];

int main(){
	#ifndef ONLINE_JUDGE
	freopen("name.in", "r", stdin);
	freopen("name.out", "w", stdout);
	#endif

	MODINT::init(1000'000'007);

	register int i,max=0,sum=0;
	register mint res=0;
	int n=read();

	for(i=0;i<n;++i) sum+=(arr[i]=read()),max=std::max(max,arr[i]);
	dp[1]=mint(sum-1)*(sum-1)/sum;
	dp[2]=dp[1]*2-1;

	for(i=2;i<max;++i)
		dp[i+1]=dp[i]*2-dp[i-1]-mint(sum-1)/(sum-i);

	for(i=0;i<n;++i) res+=dp[arr[i]];

	print(res);

	#ifndef ONLINE_JUDGE
	fclose(stdin);
	fclose(stdout);
	#endif
    return 0;
}

谢谢你,读到这里。

本文同步自洛谷专栏,保留原始正文与发布时间。在洛谷查看原文 ↗

搜索文章

输入关键词,发现一些值得阅读的文字。

探索文字里的小小世界esc 关闭