洛谷 P3643

题面传送门

一道难度中等的 (dp)(虽然我没有想出来/kk)。

首先一眼 (dp_{i,j}) 表示考虑到第 (i) 个学校,第 (i) 个学校派出了 (j) 个划艇的方案数,转移也异常显然,枚举上一个派出游艇的学校以及它派出的划艇个数,那么有 (dp_{i,j}=sumlimits_{k<i}sumlimits_{l<j}dp_{k,l}),这样暴力复杂度是 (n^2A^2),其中 (A=max{a_i,b_i}),可以使用前缀和优化,但照样不能通过,复杂度 (n^2A) 或者 (nA)

考虑优化,一个非常简单的想法是离散化,这样值域大小可以降到 (mathcal O(n)),具体来说我们将所有 (b_i)(1),这样每个学校派出游艇个数的范围就是一个左闭右开的区间 ([a_i,b_i)),我们将它离散化一下然后用一个数表示一段左开右闭的区间,然后设 (dp_{i,j}) 表示到第 (i) 个学校,第 (i) 个学校派出的游艇个数在第 (j) 个区间中的方案数,不过这样就不太好直接按照上面的方式转移了:对于上一个派出游艇的学校,如果它派出的游艇个数不在第 (j) 个区间中那倒还好,可以直接加上去,但如果它派出的游艇个数刚好在第 (j) 个区间中,那就没啥办法直接计算贡献了。因此我们换个枚举方式:枚举上一个派出了游艇,且派出游艇个数不在第 (j) 个区间中的学校 (k),以及它派出的游艇个数所在的区间 (l),假设第 (j) 个区间长度为 (len)((k,l]) 中有 (c) 个学校派出游艇个数取值范围与第 (j) 个区间有交,那么方案数就是 (sumlimits_{t=0}^{c-1}dbinom{c-1}{t} imesdbinom{len}{t+1}=sumlimits_{t=0}^{c-1}dbinom{c-1}{c-1-t} imesdbinom{len}{t+1}),根据范德蒙德卷积,后面那东西就是 (dbinom{c-1+len}{c}),于是 (dp_{i,j}=sumlimits_{k<i}sumlimits_{l<j}dp_{k,l} imesdbinom{c-1+len}{c}),第二维 (l) 显然可以前缀和优化掉,复杂度 (n^3)

const int MAXN=500;
const int MOD=1e9+7;
int n,a[MAXN+5],b[MAXN+5],key[MAXN*2+5],cnt=0,uni[MAXN*2+5],num=0;
int dp[MAXN+5],inv[MAXN+5];
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++){
		scanf("%d%d",&a[i],&b[i]);
		key[++cnt]=a[i];key[++cnt]=++b[i];
	} sort(key+1,key+cnt+1);
	for(int i=1;i<=cnt;i++) if(key[i]^key[i-1]) uni[++num]=key[i];
	for(int i=1;i<=n;i++){
		a[i]=lower_bound(uni+1,uni+num+1,a[i])-uni;
		b[i]=lower_bound(uni+1,uni+num+1,b[i])-uni;
	} dp[0]=1;
	for(int i=(inv[0]=inv[1]=1)+1;i<=n;i++) inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
	for(int i=1;i<num;i++){
		int len=uni[i+1]-uni[i];
		for(int j=n;j;j--) if(a[j]<=i&&i<b[j]){
			for(int k=j-1,cur=len,cnt=1;~k;k--){
				dp[j]=(dp[j]+1ll*cur*dp[k])%MOD;
				if(a[k]<=i&&i<b[k]) ++cnt,cur=1ll*cur*(len+cnt-1)%MOD*inv[cnt]%MOD;
			}
		}
	} int ans=0;
	for(int i=1;i<=n;i++) ans=(ans+dp[i])%MOD;
	printf("%d
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/ET2006/p/luogu-P3643.html