F. Good Contest(DP+组合数学)

F. Good Contest

在这里插入图片描述
input

3
1 2
1 2
1 2

output

499122177

input

2
42 1337
13 420

output

578894053

input

2
1 1
0 0

output

1

input

2
1 1
1 1

output

1

Note
The real answer in the first test is 12.

题目大意

给你n个题目,每个题目给出最少过题人数和最多过题人数,问你从第一道题开始过题数为非递增序列的概率为多少。

解题思路

根据题目我们可知 n 很小,但是 n 的每一个区间很大,因此我们需要进行离散化处理,之后我们用dp[i][j] 表示第i道题,在过 j 以内个人的方案有几种,然后一直递归下去就可以了,这时候的转移方程为

在这里插入图片描述

在第 i 道题以前的题,过题人数都比 j 大,那么只要dp[k][j+1] *和这个组合公式就行了;这个公式代表的是n个球,每个球可以取走多次,问你去K次,一共有多少种取法,答案是 C(n+k-1,k),用到这里表示的意义是,离散化的时候每个相邻的数之间是有多个的,在前前面第q个数,这是他可以从这个长度里面多加k个值,这k个值要构成非递增序列,和这个问题很相似。似乎说了一大堆废话,但是希望大家手动模拟下就会了解点了。
这里推荐一篇大佬的博客,感觉写的也挺好

代码

#include<bits/stdc++.h>
using namespace std;
#define ll long long
vector<ll>ve;
ll l[110],r[110];
ll d[55][110];
const ll mod=998244353;

ll qoww(ll a,ll b)
{
	ll ans=1;
	while(b)
	{
		if(b&1) ans=(ans*a)%mod;
		a=(a*a)%mod;
		b>>=1;
	}
	return ans;
}
//求组合数 
ll zu_he(ll n,ll m)
{
	if(m>n) return 0;
	ll nn=1;ll mm=1;
	for(ll i=1;i<=m;i++) mm=(mm*i)%mod;
	for(ll j=n;j>=n-m+1;j--) nn=(nn*j)%mod;
	return nn*qoww(mm,mod-2)%mod;
}
int main()
{
	int n;
	cin>>n;
	ve.push_back(-1);
	for(int i=1;i<=n;i++)
	{
		cin>>l[i]>>r[i];
		ve.push_back(l[i]);
		ve.push_back(r[i]+1);
	} 
	// 离散化处理 
	sort(ve.begin(),ve.end());
	ve.erase(unique(ve.begin(),ve.end()),ve.end());
	for(int i=1;i<=n;i++)
	{
		l[i]=lower_bound(ve.begin(),ve.end(),l[i])-ve.begin();
		r[i]=lower_bound(ve.begin(),ve.end(),r[i]+1)-ve.begin()-1;
	}
	
	for(int i=0;i<ve.size();i++) d[0][i]=1;//初始化第一行 
	for(int i=1;i<=n;i++)
	{
		for(int j=l[i];j<=r[i];j++)//区间的长度 
		{
//       这个 for 循环是代码的核心,不懂的话多看几遍并且模拟一遍			
			for(int k=i-1;k>=0;k--)//每次都和上一次进行计算,然后更新。 
			{
				d[i][j]+=d[k][j+1]*zu_he(ve[j+1]-ve[j]+i-k-1,i-k)%mod;
//这列涉及一个组合数学 n个球,取出 k 个,每个可以取多次。
//问你有几中取法,就是 C(n+k-1,k);					
				d[i][j]%=mod;
				if(l[k]>j||r[k]<j)break;
			}
		}
		for(int j=ve.size()-1;j>=0;j--) {// 这里倒着加因为后面的数越小方案数越多嘛 
				d[i][j]=(d[i][j]+d[i][j+1])%mod;
			}
	}
	ll ans=d[n][0];
	//最后除以分母 
	for(int i=1;i<=n;i++) ans=(ans*qoww(ve[r[i]+1]-ve[l[i]],mod-2))%mod;
	cout<<ans<<"\n";
	return 0;
} 
发布了49 篇原创文章 · 获赞 14 · 访问量 4337

猜你喜欢

转载自blog.csdn.net/qq_43750980/article/details/104149334