首页 > 其他分享 >P3643 [APIO2016] 划艇

P3643 [APIO2016] 划艇

时间:2024-03-15 11:55:23浏览次数:32  
标签:派出 return int APIO2016 P3643 学校 划艇 mod

题意:

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

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

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

\(N \le 500,a_{i},b_{i} \le 10^9\)。

分析:

由于值域有 \(10^9\),因此需要离散化成一个个小段。

发现 dp 的难点在于每个学校可以不用派出划艇。

不如利用将每个学校分成连续的一段,来刻画这个过程,在同一段的学校派出的潜艇颜色为同一段(也可以不派出),并钦定每一段的最后一个学校必选

因此记 \(f_{i,j}\) 表示考虑前 \(i\) 个学校,并且第 \(i\) 个学校选颜色段数编号为 \(j\)。

转移可以枚举这一段的开始的学校 \(l\),和上一段的结尾颜色段数编号 \(k\)。

\[f_{i,j}=\sum_{l=1}^{i} \tbinom{m+L-1}{m} \sum_{k=1}^{j-1}f_{l-1,k} \]

其中 \(\tbinom{m+L-1}{m}\) 表示将 \(m\) 个物品放进 \(L\) 个位置,每个物品可以不放在任何一个位置,且不能为空的方案数量。考虑构造

\[0\ 0\ 0...0 \ 1 \ 2...L(共 \ m-1 \ 个 \ 0) \]

选第 \(i\) 个 \(0\) 表示第 \(i\) 个物品不放,选 \(x\ (x \ne 0)\) 表示第 \(x\) 个不选 \(0\) 的物品放在 \(x\) 这个位置上。

而转移中 \(\sum_{k=1}^{j-1}f_{l-1,k}\) 可以用前缀和优化。

因此时间复杂度 \(O(n^3)\)。

代码:

#include<bits/stdc++.h>
#define int long long
#define N 3010
#define mod 1000000007
using namespace std;
int Pow(int a, int n) {
	if(n == 0) return 1;
	if(n == 1) return a;
	int x = Pow(a, n / 2);
	if(n % 2 == 0) return x * x % mod;
	else return x * x % mod * a % mod;
}
int inv(int x) {
	return Pow(x, mod - 2);
}
int n, ans;
struct node {
	int l, r;
}a[N];
int b[N], tot, c[N], len, L[N], R[N];
int h[N][N], fac[N], Inv[N], f[N][N], g[N][N];
int C(int j, int m) {
	return h[j][m] * Inv[m] % mod;
}
signed main() {
	cin >> n;
	for(int i = 1; i <= n; i++) {
		cin >> a[i].l >> a[i].r;
		b[++tot] = a[i].l;
		b[++tot] = a[i].r;
	}
	sort(b + 1, b + tot + 1);
	for(int i = 1; i <= tot; i++) 
		if(i == 1 || b[i] != b[i - 1]) c[++len] = b[i];
	int H = 0;
	for(int i = 1; i < len; i++) {
		H++;
		L[H] = c[i];
		R[H] = c[i];
		if(c[i] + 1 <= c[i + 1] - 1) {
			H++;
			L[H] = c[i] + 1;
			R[H] = c[i + 1] - 1;
		}
	}
	H++;
	L[H] = c[len];
	R[H] = c[len];
	len = H;
	for(int i = 1; i <= len; i++) {
		h[i][0] = 1;
		int x = R[i] - L[i] + 1;
		for(int j = 1; j <= n; j++) h[i][j] = h[i][j - 1] * (x + j - 1) % mod;
	}
	fac[0] = Inv[0] = 1;
	for(int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i % mod;
	Inv[n] = inv(fac[n]);
	for(int i = n - 1; i >= 1; i--) Inv[i] = Inv[i + 1] * (i + 1) % mod;
	f[0][0] = 1;
	for(int i = 0; i <= len; i++) g[0][i] = 1;
	for(int i = 1; i <= n; i++) {
		for(int j = 1; j <= len; j++) { //枚举选什么
			g[i][j] = g[i][j - 1];
			if(a[i].l <= L[j] && R[j] <= a[i].r) ;
			else continue;
			int m = 0;
			for(int ll = i; ll >= 1; ll--) {
				if(a[ll].l <= L[j] && R[j] <= a[ll].r) m++;
				f[i][j] += g[ll - 1][j - 1] * C(j, m) % mod;
				f[i][j] %= mod;
			}
			ans = (ans + f[i][j]) % mod;
			g[i][j] = (g[i][j] + f[i][j]) % mod;
		}
	}
	cout << ans;
	return 0;
}

标签:派出,return,int,APIO2016,P3643,学校,划艇,mod
From: https://www.cnblogs.com/xcs123/p/18075110

相关文章

  • P3643 [APIO2016] 划艇
    题意给定数列\(a,b\),试求出序列\(S\)的方案数,使得:\(a_i\leS_i\leb_i,S_{i-1}<S_i\)或\(S_i=0\)。\(S\)不能是全\(0\)序列。\(a_i,b_i\le10^9,n\le500\)Sol不难想到一个trivial的思路。设\(f_{i,j}\)表示确定了\(S_1\toS_i\),并且\(S......
  • 【APIO2016】烟火表演
    先前的题目对slopetrick的认识还不深刻,这题可以看出一个完整的构建过程。题目描述给定一棵有根树,根为\(1\),边带权,修改边权的代价时修改值与原值差的绝对值,求让所有叶子到根距离相等的最小代价。\(1\leqn\leq3\times10^5,1\leqw\leq10^9\)。算法解析首先有朴......
  • P3643 [APIO2016] 划艇
    [APIO2016]划艇-洛谷题目详情-[Apio2016]赛艇-BZOJbyHydroOJ看着个题目以为是变换考虑方向,但想了半天完全没有思路先考虑暴力。设\(dp_{i,j}\)表示前\(i\)个数,第\(i\)个数强制选,值为\(j\)的方案数容易得到转移方程:\[dp_{i,j}=\begin{cases}\sum\li......
  • P3643 [APIO2016] 划艇
    题意给你两个序列\(a,b\),求严格递增的序列\(c\)的个数,满足:\(\foralli,c_i\in[a_i,b_i]\)。特别的,如果\(c_i=0\)则无视当前这个\(c_i\)。Solution好困难的dp,耗我......