[BZOJ4584][Apio2016]赛艇

试题描述

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

输入

第一行包括一个整数N,表示学校的数量。接下来N行,每行包括两个正整数,用来描述一所学校。其中第行包括的两个正整数分别表示Ai,Bi(1<=Ai<=Bi<=10^9),N<=500

输出

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

输入示例

2
1 2
2 3

输出示例

7

数据规模及约定

见“输入

题解

首先离散成不超过 2n 个区间;设 f(i, j, k) 表示考虑前 i 个学校,其中最后 k 个学校在区间 j 中的方案数。

转移就是:

f(i, j, k) = f(i - 1, j, k) + f(i - 1, j, k - 1) / C(len[j], k-1) * C(len[j], k)  (k > 1)

f(i, j, 1) = f(i - 1, j, k) + ∑1≤x≤j1≤y≤x f(i - 1, x, y)

其中,len[j] 表示第 j 段区间的长度,C(a, b) 表示组合数(从 a 元素中选 b 个的方案数)。

对于第二个式子搞一下前缀和优化就好了。

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cctype>
#include <algorithm>
using namespace std;

const int BufferSize = 1 << 16;
char buffer[BufferSize], *Head, *Tail;
inline char Getchar() {
	if(Head == Tail) {
		int l = fread(buffer, 1, BufferSize, stdin);
		Tail = (Head = buffer) + l;
	}
	return *Head++;
}
int read() {
	int x = 0, f = 1; char c = Getchar();
	while(!isdigit(c)){ if(c == ‘-‘) f = -1; c = Getchar(); }
	while(isdigit(c)){ x = x * 10 + c - ‘0‘; c = Getchar(); }
	return x * f;
}

#define maxn 510
#define MOD 1000000007
#define LL long long

int n, num[maxn<<1], cntn;
struct Line {
	int l, r;
	Line() {}
	Line(int _, int __): l(_), r(__) {}
	int len() { return r - l + 1; }
} sch[maxn], ls[maxn<<1];

int invnum[maxn], C[maxn<<1][maxn], invC[maxn<<1][maxn];
void gcd(LL a, LL b, LL& x, LL& y) {
	if(!b){ x = 1; y = 0; return ; }
	gcd(b, a % b, y, x); y -= a / b * x;
	return ;
}
int Inv(LL a) {
	LL x, y;
	gcd(a, MOD, x, y);
	return (x % MOD + MOD) % MOD;
}
void init() {
	for(int i = 1; i <= n; i++) invnum[i] = Inv(i);
	for(int i = 1; i < cntn; i++) {
		C[i][0] = invC[i][0] = 1;
		for(int j = 1; j <= min(ls[i].len(), n); j++)
			C[i][j] = (LL)C[i][j-1] * (ls[i].len() - j + 1) % MOD * invnum[j] % MOD,
			invC[i][j] = Inv(C[i][j]);
	}
	return ;
}

int f[maxn<<1][maxn], sum[maxn<<1];

int main() {
	n = read();
	for(int i = 1; i <= n; i++) {
		int l = read(), r = read() + 1;
		sch[i] = Line(l, r);
		num[++cntn] = l; num[++cntn] = r;
	}
	sort(num + 1, num + cntn + 1);
	cntn = unique(num + 1, num + cntn + 1) - num - 1;
	for(int i = 1; i < cntn; i++) ls[i] = Line(num[i], num[i+1] - 1);
	for(int i = 1; i <= n; i++)
		sch[i].l = lower_bound(num + 1, num + cntn + 1, sch[i].l) - num,
		sch[i].r = lower_bound(num + 1, num + cntn + 1, sch[i].r) - num;

	init();
	for(int j = 0; j < cntn; j++) sum[j] = 1;
	for(int i = 1; i <= n; i++) {
		for(int j = sch[i].r - 1; j >= sch[i].l; j--) {
			for(int k = min(i, ls[j].len()); k >= 2; k--) {
				f[j][k] += (LL)f[j][k-1] * invC[j][k-1] % MOD * C[j][k] % MOD;
				if(f[j][k] >= MOD) f[j][k] -= MOD;
			}
			f[j][1] += (LL)sum[j-1] * ls[j].len() % MOD;
			if(f[j][1] >= MOD) f[j][1] -= MOD;
		}
		sum[0] = 1;
		for(int j = 1; j < cntn; j++) {
			sum[j] = sum[j-1];
			int mxk = min(i, ls[j].len());
			for(int k = 1; k <= mxk; k++) {
				sum[j] += f[j][k];
				if(sum[j] >= MOD) sum[j] -= MOD;
			}
		}
	}

	printf("%d\n", ((sum[cntn-1] - sum[0]) % MOD + MOD) % MOD);

	return 0;
}

终于卡过去了。。。

时间: 2024-10-10 00:01:58

[BZOJ4584][Apio2016]赛艇的相关文章

BZOJ 4584 [Apio2016]赛艇 ——动态规划

Subtask 1 直接$N^2$ $DP$,就可以了 Subtask 2 用$f[i][j]$表示当前位置为$i$,结束元素为$j$的方案数. Subtask 3 看下面 Subtask 4 首先可以枚举一段序列选择同一个重叠的区间,然后一些可以不选,如果选的话要求上升. 然后很容易得到方程,离散化之后就可以$N^4$直接做了. 然后并不会优化,看了题解. 想了想写不出来. 抄代码啦! #include <map> #include <ctime> #include <cm

APIO2016赛艇

首先每个学校的边界范围是\(1e9\),肯定不能直接\(dp[i][j]\)表示前i所学校,第\(i\)所学校派出\(j\)艘船,但\(b<=500\)所有考虑把\(a,b\)离散,第二维改为当前这个学校派出数量在那个区间里 转移的时候,如果前面所有的学校都不在此区间内,那贡献为\(\sum_{i'=1}^{i}\sum_{j'=1}^{j}dp[i'][j']\) 但是如果有学校要选在该区间内,问题便转化为在一段长度为len的区间内选出k个数,使他们递增,当然,有些数是可以不选的,该方案数为\

BZOJ 4584 【APIO2016】 赛艇

题目链接:赛艇 讲道理好好的Boat为啥要翻译成赛艇呢--题面中不也是划艇么-- 这道题考虑一下dp.由于划艇数量过于庞大,所以肯定不能直接记录到dp状态中.所以一个想法就是把数量离散化,然后把每个学校的数量在哪一段内记录下来.也就是说\(f_{i,j,k}\)表示前\(i\)个学校,第\(i\)所学校派出的划艇数量在区间\(j\)内,并且区间\(j\)内共有\(k\)个学校的方案数.然后分类讨论一下转移: 当\(k\ne 1\)时,有: \begin{aligned}f_{i,j,k} &=\

CTSC2016&amp;&amp;APIO2016爆零记

CTSC2016&&APIO2016爆零记 前言:人生第一次写(骗)爆(访)零(问)记(量),心里还是有点小激动呢.不过由于本文作者语文水平低下,如果发现用词不当或只会记流水账,请谅解. CTSC Day0: CCF大发慈悲让我们入住了5星级酒店昆(百)泰酒店. 不过也有一点不兹磁的地方,就是酒店离学校大概要步行半个小时,对于我这种懒人大概是天大的灾难吧(雾).于是这次比赛就强行变成了CTSC&&APIO远足营. 第一次看见70块钱一瓶零下八度不结冰的矿泉水,然而家穷喝不起

【APIO2016】Gap

[APIO2016]Gap 交互题 对于30%的数据,直接从s=0,t=1e18开始,每次让s=mn+1,t=mx-1,每一次可以得到两个,可以在(n+1)/2的时间内得到 对于其他数据,我们先求出最大最小值,求出len=(mx-mn)/(n-1),这相当与是一个"平均数",如果我将区间分为若干段长度为len的区间,只记录每个区间内的最大值和最小值,这样被漏算的那些点,他们最多有len-1的贡献,而这个还达不到平均数,所以肯定不会影响答案,所以就可以在3*n以内求出序列有用的部分了.

计数难题5:[APIO2016]划艇

计数难题5:[APIO2016]划艇 标签(空格分隔): 计数难题题选 题目大意: 给定\(n\)个区间\([l_i,r_i]\). 你可以从第\(i\)个区间中选出一个整数元素\(a_i\in [l_i,r_i]\),也可以不选. 要求选出的元素按标号顺序排列后构成一个严格单调递增序列. 求至少选出一个元素的合法方案数. 答案对\(10^9+7\)取模. 数据范围:\(n\leq 500\) , \(l_i\leq r_i \leq 10^9\) . 题解 可以想到把区间离散化. 设\(f_{

CTSC2016&amp;&amp;APIO2016滚粗记&amp;&amp;酱油记&amp;&amp;游记(持续更新)

挖一波坑 #include <cstdio> using namespace std; int main(){ puts("转载请注明出处:http://www.cnblogs.com/wangyurzee7/"); puts("谢谢您的配合"); puts("by wangyurzee7"); return 0; } Day-1 昨天莫名其妙就感冒了... 在家养生了一天,看了一上午的球,下午鏼了几题51nod,拿钱基本无望了,收拾

UOJ #206. 【APIO2016】Gap

有 NN 个严格递增的非负整数 a1,a2,…,aNa1,a2,…,aN(0≤a1<a2<?<aN≤10180≤a1<a2<?<aN≤1018).你需要找出 ai+1−aiai+1−ai(0≤i≤N−10≤i≤N−1)里的最大的值. 你的程序不能直接读入这个整数序列,但是你可以通过给定的函数来查询该序列的信息.关于查询函数的细节,请根据你所使用的语言,参考下面的实现细节部分. 你需要实现一个函数,该函数返回 ai+1−aiai+1−ai(0≤i≤N−10≤i≤N−1)中

[APIO2016]

2016的题貌似是韩国棒子出的,好丧啊.... 看了题解还想了好久...... ------------------------------------------------- A.Boat 有n个数,每个数字可取[li,ri]内的任意整数si,但是要求对于任意i<j,都有si<sj,求方案数  n<=500,l,r<=10^9 题解:首先离散,然后不同区间的方案数很好转移,我们考虑相同区间的方案数,发现是一个差分了多次的数列,如果区间长度是l,选m个这样的区间,那么方案数是11