BZOJ4584: [Apio2016]赛艇

Description

在首尔城中,汉江横贯东西。在汉江的北岸,从西向东星星点点地分布着个划艇学校,编号依次为到。每个学校都
拥有若干艘划艇。同一所学校的所有划艇颜色相同,不同的学校的划艇颜色互不相同。颜色相同的划艇被认为是一
样的。每个学校可以选择派出一些划艇参加节日的庆典,也可以选择不派出任何划艇参加。如果编号为的学校选择
派出划艇参加庆典,那么,派出的划艇数量可以在Ai至Bi之间任意选择(Ai<=Bi)。值得注意的是,编号为i的学
校如果选择派出划艇参加庆典,那么它派出的划艇数量必须大于任意一所编号小于它的学校派出的划艇数量。输入
所有学校的Ai、Bi的值,求出参加庆典的划艇有多少种可能的情况,必须有至少一艘划艇参加庆典。两种情况不同
当且仅当有参加庆典的某种颜色的划艇数量不同

Input

 第一行包括一个整数N,表示学校的数量。接下来N行,每行包括两个正整数,用来描述一所学校。其中第行包括的

两个正整数分别表示Ai,Bi(1<=Ai<=Bi<=10^9),N<=500

Output

 输出一行,一个整数,表示所有可能的派出划艇的方案数除以1,000,000,007得到的余数

Sample Input

2
1 2
2 3

Sample Output

7

HINT

Source

DP
http://m.blog.csdn.net/article/details?id=51674707
看了题解才会 cyz:反正我10分钟就想出来了
考虑子问题:如果给的是N个长度为len的区间,可以全部不选,问选法?
那么选k个区间的方案数C(n,k)*C(len,k)
求和
转换一下变成∑C(n,n-k)*C(len,k)
就是n+len个数选n个出来,左边取n-k个,右边取k个的具体形式
答案是C(n+len,n)
接下来,考虑离散化区间
然后dp[i][j]表示第i个学校在区间j的方案数
如果之前的数不在j之内
dp[i][j]+=∑dp[k][l]*len[j](k<i,l<j)
如果在j之内
枚举最早的在j的学校,记为k
dp[i][j]+=∑dp[l][m](l<k,m<j)*子问题中算的贡献
记录二维前缀和即可
#include <bits/stdc++.h>

using namespace std;

const int maxn = 502;
const int mod = 1e9 + 7;

int n, st[maxn << 1], dp[maxn][maxn << 1], l[maxn], r[maxn], m, len[maxn << 1], inv[maxn];

int main()
{
	scanf( "%d", &n );
	inv[ 0 ] = inv[ 1 ] = 1;
	for( int i = 2 ; i <= n ; i++ ) inv[ i ] = 1ll * ( mod - mod / i ) * inv[ mod % i ] % mod; 
	for( int i = 1 ; i <= n ; i++ ) scanf( "%d%d", &l[ i ], &r[ i ] ), st[ m++ ] = l[ i ], st[ m++ ] = r[ i ] + 1;
	sort( st, st + m );
	m = unique( st, st + m ) - st;
	for( int i = 1 ; i < m ; i++ ) len[ i ] = st[ i ] - st[ i - 1 ];
	for( int i = 1 ; i <= n ; i++ )
	{
		l[ i ] = upper_bound( st, st + m, l[ i ] ) - st;
		r[ i ] = upper_bound( st, st + m, r[ i ] ) - st;
	}
	for( int i = 0 ; i < m ; i++ ) dp[ 0 ][ i ] = 1;
	for( int i = 1 ; i <= n ; i++ )
	{
		dp[ i ][ 0 ] = 1;
		for( int j = l[ i ] ; j <= r[ i ] ; j++ )
		{
			dp[ i ][ j ] = 1ll * dp[ i - 1 ][ j - 1 ] * len[ j ] % mod;
			int mul = len[ j ] - 1, now = 1;
			for( int k = i - 1 ; k ; k-- )
				if( l[ k ] <= j && j <= r[ k ] )
				{
					now++;
					mul = 1ll * mul * ( len[ j ] + now - 2 ) % mod * inv[ now ] % mod;
					( dp[ i ][ j ] += 1ll * dp[ k - 1 ][ j - 1 ] * mul % mod ) %= mod;
				}
		}
		for( int j = 1; j < m ; j++ )
			dp[ i ][ j ] = ( 1ll * dp[ i ][ j ] + dp[ i - 1 ][ j ] + dp[ i ][ j - 1 ] - dp[ i - 1 ][ j - 1 ] + mod ) % mod;
	}
	return printf( "%d\n", ( dp[ n ][ m - 1 ] - 1 + mod ) % mod ), 0;
} 


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值