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

P3643 [APIO2016] 划艇

时间:2024-02-15 22:01:52浏览次数:31  
标签:le int sum APIO2016 P3643 划艇 array include mod

题意

给定数列 \(a, b\),试求出序列 \(S\) 的方案数,使得:

  • \(a_i \le S_i \le b_i, S_{i - 1} < S_i\) 或 \(S_i = 0\)。

\(S\) 不能是全 \(0\) 序列。

\(a_i, b_i \le 10 ^ 9, n \le 500\)

Sol

不难想到一个 trivial 的思路。

设 \(f_{i, j}\) 表示确定了 \(S_1 \to S_i\),并且 \(S_i = j\) 的方案数。

\[f_{0, 0} = 1 \]

\[f_{i, j} = \left \{ \begin{array}{**lr**} \sum_{i' = 0} ^ {i - 1} \sum_{j' = 0} ^ {j - 1} f_{i', j'} , & a_i \le j \le b_i \\ 0 , & otherwise\end{array} \right. \]

答案就是 \(\sum_{i}\sum_{j} f_{i,j}\)

考虑将第二维离散化掉。

\(f_{i, j}\) 表示确定了 \(S_1 \to S_i\),且 \(S_i\) 落在第 \(j\) 段的方案数。

如果两个端点都有贡献显然会算重,先先将题目中的 \([a_i, b_i]\) 转为 \([a_i, b_i)\)。

相同地,考虑去枚举上一个不在第 \(j\) 段的 \(S_{i'}\)。

设 \(m\) 表示第 \(i'\) 到 \(i\),\(a, b\) 完全包含第 \(j\) 段的个数,\(L\) 表示第 \(j\) 段的长度。

不难发现,现在的问题是如何求出在 \(L\) 中选 \(m\) 个数,使得非零数严格递增。

集中注意力,发现这个东西就是 \(\binom{L + m - 1}{m}\),上面 \(-1\) 是因为第 \(i\) 个数不能为 \(0\)。

\[f_{0, 0} = 1 \]

\[f_{i, j} = \left \{ \begin{array}{**lr**} \sum_{i' = 0} ^ {i - 1} \sum_{j' = 0} ^ {j - 1} \binom{L + m - 1}{m} f_{i', j'} , & a_i \le j \le b_i \\ 0 , & otherwise\end{array} \right. \]

考虑上面的式子。

发现 \(\binom{L + m - 1}{m}\) 与 \(j\) 无关,提出来。

\[\sum_{i' = 0} ^ {i - 1} \binom{L + m - 1}{m} \sum_{j' = 0} ^ {j - 1} f_{i', j'} \]

简单前缀和优化即可。

设 \(g_{i, j} = \sum_{j' = 0} ^ {j} f_{i, j'}\)。

\[\sum_{i' = 0} ^ {i - 1} \binom{L + m - 1}{m} g_{i', j' - 1} \]

组合数每次向杨辉三角的右下递推,这是平凡的。

时间复杂度: \(O(n ^ 3)\)

Code

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <array>
#include <vector>
#define int long long
#define pii pair <int, int>
using namespace std;
#ifdef ONLINE_JUDGE

#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
char buf[1 << 23], *p1 = buf, *p2 = buf, ubuf[1 << 23], *u = ubuf;

#endif
int read() {
	int p = 0, flg = 1;
	char c = getchar();
	while (c < '0' || c > '9') {
		if (c == '-') flg = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9') {
		p = p * 10 + c - '0';
		c = getchar();
	}
	return p * flg;
}
void write(int x) {
	if (x < 0) {
		x = -x;
		putchar('-');
	}
	if (x > 9) {
		write(x / 10);
	}
	putchar(x % 10 + '0');
}
#define fi first
#define se second

const int N = 1005, mod = 1e9 + 7;

array <array <int, N>, N> f, g;
array <pii, N> isl;

void Mod(int &x) {
	if (x >= mod) x -= mod;
	if (x < 0) x += mod;
}

namespace Mth {

array <int, N> fac, inv;

int pow_(int x, int k, int p) {
	int ans = 1;
	while (k) {
		if (k & 1) ans = ans * x % p;
		x = x * x % p;
		k >>= 1;
	}
	return ans;
}

void init(int n = 1003) {
	inv[0] = 1;
	for (int i = 1; i <= n; i++)
		inv[i] = pow_(i, mod - 2, mod);
	/*
	inv[n] = pow_(fac[n], mod - 2, mod);
	for (int i = n; i; i--)
		inv[i - 1] = inv[i] * i % mod;
		*/
}

}

signed main() {
	int n = read();
	vector <int> tp;
	Mth::init();
	for (int i = 1; i <= n; i++) {
		isl[i].fi = read(), isl[i].se = read() + 1;
		tp.push_back(isl[i].fi), tp.push_back(isl[i].se);
	}
	sort(tp.begin(), tp.end());
	tp.erase(unique(tp.begin(), tp.end()), tp.end());
	for (int i = 1; i <= n; i++) {
		isl[i].fi = lower_bound(tp.begin(), tp.end(), isl[i].fi) - tp.begin() + 1;
		isl[i].se = lower_bound(tp.begin(), tp.end(), isl[i].se) - tp.begin() + 1;
		/* write(isl[i].fi), putchar(32); */
		/* write(isl[i].se), puts(""); */
	}
	for (int i = 0; i < tp.size(); i++)
		g[0][i] = 1;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j < tp.size(); j++) {
			int l = tp[j] - tp[j - 1], m = 1, tp0 = l;
			if (isl[i].fi > j || j >= isl[i].se) continue;
			for (int _i = i - 1; ~_i; _i--) {
					/* write(tp0), puts("^"); */
				/* } */
				f[i][j] += tp0 * g[_i][j - 1] % mod, Mod(f[i][j]);
				if (_i && isl[_i].fi <= j && j < isl[_i].se)
					tp0 = tp0 * (l + m) % mod * Mth::inv[m + 1] % mod, m++;
			}
		}
		for (int j = 1; j < tp.size(); j++)
			g[i][j] = g[i][j - 1] + f[i][j], Mod(g[i][j]);
	}
	/* for (int i = 1; i <= n; i++) { */
		/* for (int j = 1; j < tp.size(); j++) */
			/* write(f[i][j]), putchar(32); */
		/* puts(""); */
	/* } */
	int ans = 0;
	for (int i = 1; i <= n; i++)
		for (int j = 1; j < tp.size(); j++)
			ans += f[i][j], Mod(ans);
	write(ans), puts("");
	return 0;
}

标签:le,int,sum,APIO2016,P3643,划艇,array,include,mod
From: https://www.cnblogs.com/cxqghzj/p/18016664

相关文章

  • 【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,耗我......