apio 2016 T1

在首尔城中,汉江横贯东西。在汉江的北岸,从西向东星星点点地分布着 NN 个划艇学校,编号依次为 11 到 NN。每个学校都拥有若干艘划艇。同一所学校的所有划艇颜色相同,不同的学校的划艇颜色互不相同。颜色相同的划艇被认为是一样的。每个学校可以选择派出一些划艇参加节日的庆典,也可以选择不派出任何划艇参加。如果编号为 ii 的学校选择派出划艇参加庆典,那么,派出的划艇数量可以在 aiai 至 bibi 之间任意选择(aibiai≤bi)。

值得注意的是,编号为 ii 的学校如果选择派出划艇参加庆典,那么它派出的划艇数量必须大于任意一所编号小于它的学校派出的划艇数量。

输入所有学校的 ai,biai,bi 的值,求出参加庆典的划艇有多少种可能的情况,必须有至少一艘划艇参加庆典。两种情况不同当且仅当有参加庆典的某种颜色的划艇数量不同。

输入格式

第一行包括一个整数 NN,表示学校的数量。

接下来 NN 行,每行包括两个正整数,用来描述一所学校。其中第 ii 行包括的两个正整数分别表示 ai,bi1aibi109ai,bi(1≤ai≤bi≤109)

输出格式

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

样例一

input
2
1 2
2 3

output
7

explanation

在只有一所学校派出划艇的情况下有 44 种方案,两所学校都派出划艇的情况下有 33 种方案,所以答案为 77

限制与约定

子任务 1199 分):1N5001≤N≤500 且对于所有的 1iN1≤i≤N,保证 ai=biai=bi

子任务 222222 分):1N5001≤N≤500 且 Ni=1(biai)106∑i=1N(bi−ai)≤106

子任务 332727 分):1N1001≤N≤100

子任务 444242 分):1N5001≤N≤500

时间限制:1s1s

空间限制:256MB


直接讲正解吧。

肯定要离散,但是离散完好像很不好搞

设计状态f[i][j][k]表示考虑到第i这个位置,然后最后取的数属于第j块,k表示第j块选了多少个数。

先考虑k>1的情况,很显然答案是f[i][j][k] = f[i][j][k-1]+在第j个块选1个数使得这个数满足大于前k-1个数,

这个的话其实就是C(lenj,k)/C(lenj,k-1),理解的话就是前者是在后者的基础上多选一个数,而组合数是不计顺序的一个排列。

可以对这个式子化简就是(lenj-k+1)/k

然后考虑k = 1的做法

首先我们可以只取这个数,那么就是f[i][j][1] = f[i-1][j][1]+lenj;

还有就是还要加上f[i-1][j'][k']的和乘上lenj,其中j'<j且k'<i这个的话用一个前缀和优化,意思大概就是之前可以选在j块之前的都可以接,然后因为在i之前,所以最多只选i-1个数,感觉这个思路很妙啊。

#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int MAXN = 1005;
const int MOD = 1000000007;
int b[MAXN], lenb, l[MAXN], r[MAXN], inv[MAXN], len[MAXN];
int f[2][MAXN][MAXN], ans;
int n, m, i, j, k, g[MAXN];
inline int get()
{
	char c;
	while ((c = getchar()) < 48 || c > 57);
	int res = c - 48;
	while ((c = getchar()) >= 48 && c <= 57)
		res = res * 10 + c - 48;
	return res;
}
inline int ksm(int x, int y, int z)
{
	int b = 1;
	while (y)
	{
		if (y & 1) b = (long long)b * x % z;
		x = x * (long long)x % z;
		y >>= 1;
	}
	return b;
}
int main()
{
	n = get();
	for(i = 1; i <= n; i ++)
		l[i] = get(), r[i] = get(), b[++lenb] = l[i], b[++lenb] = ++r[i];
	sort(b + 1, b + 1 + lenb);
	lenb = unique(b + 1, b + 1 + lenb) - 1 - b;
	for(i = 1; i <= n; i ++)
		l[i] = lower_bound(b + 1, b + 1 + lenb, l[i]) - b, r[i] = lower_bound(b + 1, b + 1 + lenb, r[i]) - b;
	int las = 1, now = 0;
	for(i = 1; i <= n * 2; i ++)
		inv[i] = ksm(i, MOD - 2, MOD);
	for(i = 1; i < lenb; i ++)
		len[i] = b[i + 1] - b[i];
	for(i = l[1]; i < r[1]; i ++)
		f[now][i][1] = b[i + 1] - b[i], g[i] += b[i + 1] - b[i];
	for(i = l[1]; i < lenb; i ++)
		g[i] += g[i - 1], (g[i] >= MOD) ? g[i] -= MOD : g[i];
	for(i = 2; i <= n; i ++)
	{
		swap(las, now);
		for(j = 1; j < lenb; j ++)
			if (j < l[i] || j >= r[i])
				for(k = 1; k < i; k ++)
					f[now][j][k] = f[las][j][k];
		for(j = l[i]; j < r[i]; j ++)
		{
			for(k = min(i, len[j]); k >= 2; k --)
				f[now][j][k] = (f[las][j][k] + (long long)f[las][j][k - 1] % MOD * (len[j] - k + 1) % MOD * inv[k]) % MOD;
			f[now][j][1] = (f[las][j][1] + (long long)(g[j - 1] + 1) * len[j]) % MOD;
		}
		for(j = l[i]; j < lenb; j ++)
		{
			g[j] = g[j - 1];
			for(k = min(i, len[j]); k >= 1; k --)
			{
				g[j] += f[now][j][k];
				if (g[j] >= MOD) g[j] -= MOD;
			}
		}
	}
	for(j = 1; j < lenb; j ++)
		for(k = 1; k <= n; k ++)
			ans += f[now][j][k], (ans >= MOD) ? ans -= MOD : ans;
	cout << ans << endl;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值