zoukankan      html  css  js  c++  java
  • CF1096E 题解

    题目链接

    题目大意

    小明在打比赛,包括小明自己一共有 (p) 名选手参赛,每个人的得分是一个非负整数。最后的冠军是得分最高的人,如果得分最高的人有多个,就等概率从这些人中选一个当冠军。
    现在小明已知了自己的得分大于等于 (r),所有选手的得分和为 (s)。求小明获胜的概率,结果对 (998244353) 取模。

    (Solution:)

    我们只需要保证小明为(TopScorer)即可,即保证剩下的人的分数都(leq)小明的分数,(ans)即为小明为(TopScorer)的方案数除以总方案数

    因为这是DP专题里面的题,我们令(f_{s,p,m})表示(p)名选手总得分为(s),每名选手分数不超过(m)的方案数,显然有:

    (f_{s,p,m}=sumlimits_{i=0}^{m}{f_{s-i,p-1,m}}),可以维护一个前缀和,但是这是(O(srp))的,显然(TLE)

    但是这启发我们我们可以考虑单独计算某个(dp)值(答案用到的(dp)状态不多)为什么我就没有被启发到

    那么(f_{s,p,m}=sumlimits_{i=0}^{p}{(-1)^{i} imes C_p^i imes C_{s+p-1-i(m+1)}^{p-1}})

    问题来了,如何理解这个式子?(天知道我为了理解它花了多长时间)

    第一种理解方式:容斥

    遇到有关最大值的问题,可以转化为“不超过”

    在此题中,即为其他人的分数都不超过小明的分数

    即计算所有情况-至少一个人超过(m+)至少两个人超过(m-)至少三个人超过(m)(+...)(奇加偶减),所以容斥系数即为((-1)^i)

    再考虑(C_p^i imes C_{s+p-1-i(m+1)}^{p-1}),首先在(p)个人中选出(i)个人,即为(C_p^i),再给每个人分配((m+1))的分数(意思即保证这些人的分数都大于(m)),然后就是允许有空盒子的隔板法了

    第二种理解方式:二项式反演

    二项式反演就是这样的很对称一个式子:

    (f_{n}=sum_{i=0}^{n}(-1)^{i} imes C_{n}^{i} imes g_{i} Longleftrightarrow g_{n}=sum_{i=0}^{n}(-1)^{i} imes C_{n}^{i} imes f_{i})

    一个很对称的常见表达为:(f_{n}=sum_{i=0}^{n} C_{n}^{i} imes g_{i} Longleftrightarrow g_{n}=sum_{i=0}^{n}(-1)^{n-i} imes C_{n}^{i} imes f_{i})

    具体证明可以看看这篇博客

    那么我们把它套到我们的式子中,设(A(x))表示(leq x)个人不超过(m)的方案数,(B(x))表示恰好有(x)个人不超过(m)的方案数,显然有:

    (A(n)=sumlimits_{i=0}^nC_n^i imes B(i)),由二项式反演:

    (B(n)=sumlimits_{i=0}^{n}{(-1)^{n-i} imes C_n^i imes A(i)}),将 (i) 全部换成 (n-i)(这么换相当于把原来的正序(sum)改成了倒序,不会影响答案),有:

    (B(n)=sumlimits_{i=0}^{n}{(-1)^{i} imes C_n^i imes A(n-i)}),(其中 (C_{n}^{n-i}=C_{n}^i),所以不变)因为有(n-i)个人不超过(m) (Longleftrightarrow)(i)个人超过(m),那么给每个人分配(m+1)分,再使用隔板法,有:

    (B(p)=sumlimits_{i=0}^{p}{(-1)^i imes C_p^i imes C_{s+p-1-i(m+1)}^{p-1}}),这样就得到了同样的结论

    回归正题,我们得到了(f_{s,p,m})的表达式后,我们尝试用它来表示(answer),那么:

    [ans=frac{sum_{t=r}^{s} sum_{q=1}^{p}left(egin{array}{c} p-1 \ q-1 end{array} ight) imes frac{1}{q} imes f_{s-q t, p-q, t-1}}{left(egin{array}{c} s-r+p-1 \ p-1 end{array} ight)}]

    问题又双叒叕来了,如何解释这个式子?

    外层循环(t)枚举的是小明的分数,即所有人的分数(leq t)
    (q)枚举的是有多少同分的最高分,(C_{p-1}^{q-1}) 描述的是从(p-1)个人((-1)意即除去小明)中选出 (q-1)个人,(frac{1}{q})即题目中同分的人均分成为(TopScorer)的概率;
    (f_{s-qt, p-q, t-1})描述的是除去最高分的人,剩下的方案数;
    最后分母 (C_{s-r+p-1}^{p-1}) 描述的是总的方案数。

    呼~终于讲完了,上代码

    (Code:)

    #include<bits/stdc++.h>
    using namespace std;
    namespace my_std
    {
    	typedef long long ll;
    	typedef double db;
    	#define pf printf
    	#define pc putchar
    	#define fr(i,x,y) for(register ll i=(x);i<=(y);i++)
    	#define pfr(i,x,y) for(register ll i=(x);i>=(y);i--)
    	#define go(x) for(ll i=head[u];i;i=e[i].nxt)
    	#define enter pc('
    ')
    	#define space pc(' ')
    	#define fir first
    	#define sec second
    	#define MP make_pair
    	const ll inf=0x3f3f3f3f;
    	const ll inff=1e15;
    	inline ll read()
    	{
    		ll sum=0,f=1;
    		char ch=0;
    		while(!isdigit(ch))
    		{
    			if(ch=='-') f=-1;
    			ch=getchar();
    		}
    		while(isdigit(ch))
    		{
    			sum=(sum<<1)+(sum<<3)+(ch^48);
    			ch=getchar();
    		}
    		return sum*f;
    	}
    	inline void write(ll x)
    	{
    		if(x<0)
    		{
    			x=-x;
    			pc('-');
    		}
    		if(x>9) write(x/10);
    		pc(x%10+'0');
    	}
    	inline void writeln(ll x)
    	{
    		write(x);
    		enter;
    	}
    	inline void writesp(ll x)
    	{
    		write(x);
    		space;
    	}
    }
    using namespace my_std;
    const ll N=5500,mod=998244353;
    ll p,r,s,mul[N],inv[N],c[N][N],ans;
    inline ll ksmod(ll a,ll b)
    {
    	ll ans=1;
    	while(b)
    	{
    		if(b&1)
    		{
    			ans=(ans*a)%mod;
    		}
    		a=(a*a)%mod;
    		b>>=1;
    	}
    	return ans;
    }
    inline ll C(ll n,ll m)
    {
    	if(m>n||m<0||n<0) return 0;
        ll res=inv[m]*inv[n-m]%mod;
        res=res*mul[n]%mod;
        return res;
    }
    inline void init()
    {
    	mul[0]=inv[0]=1;
        for(ll i=1;i<=p+s;i++) mul[i]=(mul[i-1]*i)%mod;
        inv[p+s]=ksmod(mul[p+s],mod-2);
        for(ll i=p+s-1;i;i--) inv[i]=inv[i+1]*(i+1)%mod;
    }
    inline ll f(ll s,ll p,ll m)
    {
    	if(s==0&&p==0) return 1;
    	ll ans=0,tmp=1;
    	fr(i,0,p)
    	{
    		if(s+p-1-i*(m+1)<0) break; 
    		ans=(ans+tmp*C(p,i)%mod*C(s+p-1-i*(m+1),p-1)%mod+mod)%mod;
    		tmp*=-1; 
    	}
    	return ans;
    }
    int main(void)
    {
    	p=read(),s=read(),r=read();
    	init();
    	fr(t,r,s) fr(q,1,p) ans=(ans+(C(p-1,q-1)*ksmod(q,mod-2))%mod*f(s-q*t,p-q,t-1))%mod;
    	ans=(ans*ksmod(C(s-r+p-1,p-1),mod-2))%mod;
    	writeln(ans); 
    	return 0;
    }
    

    所以这道题正解和dp有什么关系?

    完结撒花!!!

  • 相关阅读:
    c# 基本值类型及其默认值
    军史馆如何营造更好的意境
    部队营区生态环境设计与文化氛围营造
    我的第一个CAD程序
    经典SQL语句大全
    带图标和多行显示的ListBox
    CAD 二次开发 -- 自动加载开发的DLL
    Git使用总结
    OpenStack迁移虚拟机流程分析
    OpenStack创建虚拟机流程
  • 原文地址:https://www.cnblogs.com/lgj-lgj/p/12619524.html
Copyright © 2011-2022 走看看