2020 CCPC-Wannafly Winter Camp Day6 (Div.1&2) D. 递增递增(区间dp/填坑dp)

题目

n(2<=n<=50)个区间,第i个区间[li,ri](0<=li

你可以从第i个区间里选取一个数放在新序列的第i位,所构成一个长度为n的新序列

若新序列是非严格单调递增序列,则将其所有元素的和加到答案里,求最终的答案。

思路来源

https://blog.csdn.net/qq_43202683/article/details/104069286

https://blog.csdn.net/Code92007/article/details/104120403(edu Round81 F题)

题解

wls口中的填坑dp,是一种比较新的题型,补了一下eduRound的F又回来补了一下这题

其实不知道该归到哪一类,貌似是区间dp和组合数学的范畴

dp[i][j]表示当前选了i个数,所有的数都小于等于第j个区间的方案数

sum[i][j]表示当前选了i个数,所有的数都小于等于第j个区间的合法方案的和

dp[i][j]的方案数推导详见edu Round81 F题(思路来源第二篇),一模一样的,就不细说了

 

考虑从[l,r]内多选一个数对sum的和的贡献,是期望意义的(l+r)/2,

一个数的时候好理解,多个数之间为什么加一个还是(l+r)/2,不会影响既得方案,

具体证明大概可以口胡……

比如,111 112 113 122 123 133 222 223 233 333,其平均数(ave)是2,

那么,对于一个非严格单增序列(a,b,c),

一定可以找到其对应的非严格单增序列(2*ave-c,2*ave-b,2*ave-a),两两配对之后的平均数是ave,

自耦的情况,(a,b,c)=(c,b,a),本身不用配对也成立,故所有情况均成立

 

sum[i][j]=((sum[i][j]+1ll*sum[k-1][j-1]*C%mod)%mod+1ll*dp[k-1][j-1]*C%mod*ave%mod*num%mod)%mod;

当前区间合法方案序列的和=前一区间合法方案序列的和*当前区间有多少种方案能续在前一区间的和上+本区间内额外增加的序列的和*本区间内得到这些和有多少种选法*之前有多少种方案可以往后续序列

其中,本区间内额外增加的序列的和,比如选y个,每个贡献是(l+r)/2,则该和=y*(l+r)/2

注意取模的细节问题,不然可能爆long long

代码

#include
using namespace std;
typedef long long ll;
const int N=52,M=4*N,mod=998244353; 
//dp[i][j]代表当前选了i个数 所有的数都选自离散化后小于等于第j个区间的方案数 
int n,cnt;
ll inv2,x[M],dp[N][M],sum[N][M],l[N],r[N];
ll modpow(ll x,ll n)
{
	ll res=1;
	for(;n;n>>=1,x=1ll*x*x%mod)
	if(n&1)res=1ll*res*x%mod;
	return res;
}
ll inv(ll x)
{
	return modpow(x,mod-2);
}
int main()
{
	inv2=inv(2);
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
	{
		scanf("%lld",&l[i]);
		x[++cnt]=l[i];
	}
	for(int i=1;i<=n;++i)
	{
		scanf("%lld",&r[i]);r[i]++;
		x[++cnt]=r[i];
	}
	sort(x+1,x+cnt+1);
	cnt=unique(x+1,x+cnt+1)-(x+1);
	for(int i=1;i<=n;++i)
	{
		l[i]=lower_bound(x+1,x+cnt+1,l[i])-x;
		r[i]=lower_bound(x+1,x+cnt+1,r[i])-x;
	}
	for(int j=0;j<=cnt;++j)
	{
		dp[0][j]=1;
		sum[0][j]=0;
	}
	for(int i=1;i<=n;++i)
	{
		for(int j=l[i];j

 

你可能感兴趣的:(#,区间dp,填坑dp,区间dp,组合数学)