[Luogu P3643] [BZOJ 4584] [LOJ 2567] [APIO2016]划艇

洛谷传送门
BZOJ传送门
LOJ传送门

题目描述

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

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

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

输入输出格式

输入格式:

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

接下来 N N N 行,每行包括两个正整数,用来描述一所学校。其中第 $i $行包括的两个正整数分别表示 a i , b i a_i,b_i ai,bi 1 ≤ a i ≤ b i ≤ 1 0 9 1 \leq a_i \leq b_i \leq 10^9 1aibi109)。

输出格式:

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

输入输出样例

输入样例#1:
2
1 2
2 3
输出样例#1:
7

说明

【样例解释】

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

【数据范围】

子任务 1 1 1 9 9 9 分): 1 ≤ N ≤ 500 1 \leq N \leq 500 1N500 且对于所有的 1 ≤ i ≤ N 1 \leq i \leq N 1iN,保证 a i = b i a_i=b_i ai=bi

子任务 2 2 2 22 22 22 分): 1 ≤ N ≤ 500 1 \leq N \leq 500 1N500 ∑ i = 1 N ( b i − a i ) ≤ 1 0 6 \sum_{i=1}^N (b_i-a_i) \leq 10^6 i=1N(biai)106

子任务 3 3 3 27 27 27 分): 1 ≤ N ≤ 100 1 \leq N \leq 100 1N100

子任务 4 4 4 42 42 42 分): 1 ≤ N ≤ 500 1 \leq N \leq 500 1N500

解题分析

P K U W C   D a y 2 T 1   S u b t a s k 2 PKUWC\ Day2T1\ Subtask2 PKUWC Day2T1 Subtask2来填坑了…

首先有个复杂度很不对的 d p dp dp d p [ i ] [ j ] dp[i][j] dp[i][j]表示第 i i i个学校派出 j j j艘赛艇的方案数。

然后我们注意到虽然区间很大, 但学校数不多, 所以可以离散化出这些区间, 可以设 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]表示考虑到第 i i i个学校, 其在第 j j j块选到了第 k k k个位置的方案数。

但我们发现只要记录了选到哪个位置, d p dp dp复杂度就不太对。 所以直接设 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]表示考虑到第 i i i个点, 其派出的赛艇数量在第 j j j块, 且数量在第 j j j块的学校总共有 k k k个的学校的组成不同的方案数。 换句话说, 我们现在不关心具体放在哪里了, 只关心有哪些学校在这一块里。 这样的话因为每次考虑加入新学校后是和前面的方案完全不同的, 直接一个组合数就可以统计出贡献。

然后我们发现第 i i i个学校可以从 i − 1 , i − 2 , . . . , 1 i-1,i-2,...,1 i1,i2,...,1个学校对应块的状态转移过来, 直接一个前缀和优化就好了。

代码如下:

#include <cstdio>
#include <cstring>
#include <cctype>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#define R register
#define IN inline
#define W while
#define MX 1005
#define MOD 1000000007
#define gc getchar()
#define ll long long
template <class T>
IN void in(T &x)
{
	x = 0; R char c = gc;
	for (; !isdigit(c); c = gc);
	for (;  isdigit(c); c = gc)
	x = (x << 1) + (x << 3) + c - 48;
}
int n, ans, dif;
int pos[MX], sum[MX][MX], f[MX][MX], inv[MX], lef[MX], rig[MX], pre[MX];
int main(void)
{
	in(n);
	for (R int i = 1; i <= n; ++i)
	{
		in(lef[i]), in(rig[i]);
		pos[++dif] = lef[i] - 1;
		pos[++dif] = rig[i];
	}
	inv[0] = inv[1] = 1;
	for (R int i = 2; i <= n; ++i) inv[i] = 1ll * inv[MOD % i] * (MOD - MOD / i) % MOD;
	std::sort(pos + 1, pos + 1 + dif);
	dif = std::unique(pos + 1, pos + 1 + dif) - pos - 1;
	for (R int i = 2; i <= dif; ++i)
	{
		int len = pos[i] - pos[i - 1];
		f[i][0] = 1;
		for (R int j = 1; j <= n; ++j)
		f[i][j] = 1ll * f[i][j - 1] * inv[j] % MOD * (len - j + 1) % MOD;
	}
	int ans = 0;
	for (R int i = 1; i <= n; ++i)
	{
		int s = 1;//start from current point
		for (R int j = 2; j <= dif; ++j)
		{
			int tmp = s; (s += pre[j]) %= MOD;
			if (lef[i] <= pos[j - 1] + 1 && rig[i] >= pos[j])
			{
				for (R int k = i - 1; k; --k)
				{
					(sum[j][k + 1] += sum[j][k]) %= MOD;
					(ans += 1ll * sum[j][k] * f[j][k + 1] % MOD) %= MOD;
					(pre[j] += 1ll * sum[j][k] * f[j][k + 1] % MOD) %= MOD;
				}
				(ans += 1ll * tmp * (pos[j] - pos[j - 1]) % MOD) %= MOD;
				(pre[j] += 1ll * tmp * (pos[j] - pos[j - 1]) % MOD) %= MOD;
				(sum[j][1] += tmp) %= MOD;
			}
		}
	}
	printf("%d", ans);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值