洛谷 P3643 - [APIO2016]划艇(dp)

题面传送门

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

首先一眼 \(dp_{i,j}\) 表示考虑到第 \(i\) 个学校,第 \(i\) 个学校派出了 \(j\) 个划艇的方案数,转移也异常显然,枚举上一个派出游艇的学校以及它派出的划艇个数,那么有 \(dp_{i,j}=\sum\limits_{k<i}\sum\limits_{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\) 个区间有交,那么方案数就是 \(\sum\limits_{t=0}^{c-1}\dbinom{c-1}{t}\times\dbinom{len}{t+1}=\sum\limits_{t=0}^{c-1}\dbinom{c-1}{c-1-t}\times\dbinom{len}{t+1}\),根据范德蒙德卷积,后面那东西就是 \(\dbinom{c-1+len}{c}\),于是 \(dp_{i,j}=\sum\limits_{k<i}\sum\limits_{l<j}dp_{k,l}\times\dbinom{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\n",ans);
	return 0;
}
posted @ 2021-06-26 10:56  tzc_wk  阅读(129)  评论(3编辑  收藏  举报