P3643 [APIO2016] 划艇 (线性 dp+离散化+组合数)

P3643 [APIO2016] 划艇

线性 dp+离散化+组合数

一个很朴素的状态就是设 fi,j 表示前 i 所学校排了 j 艘潜艇的方案数。但是 j109,过不了一点。

考虑每个学校对应数轴上一个区间,发现题目只关心大小关系,所以考虑离散化。这里离散化的端点为 aibi+1,表示 [ai,bi+1),这样子离散化完每个区间起始位置不需要再次增减下标,方便

那么状态也变化为 fi,j 表示考虑前 i 所学校,第 i 所学校所派划艇数量在编号 j 区间的方案数。由于前 i 所学校的区间编号一定 j,所以枚举一段编号为 j 的后缀 [k+1,i],那么前面的状态就是 fk,p

转移方程:

fi,j=k=0i1p=0j1fk,p×val(k+1,i,j)=k=0i1val(k+1,i,j)p=0j1fk,p

p<jfk,p 这段可以前缀和优化。val(i,j,k) 表示 [i,j] 区间内的学校派出的划艇数量都在 k 区间的方案数(不包括不派出划艇的学校)。那么如何求出 [k+1,i] 学校的方案数 val(k+1,i,j) ?首先第 i 所学校决策固定,设包含 j 区间的学校数量为 n(不包含 i),那么设贡献式子为:

i=0nCniCli+1=i=0nCnniCli+1=Cn+ln+1

这里组合数太大,可以从后往前枚举动态维护这个组合数。

复杂度 O(n3)

#include <bits/stdc++.h>
#define pii std::pair<int, int>
#define fi first
#define se second
#define pb push_back

using i64 = long long;
using ull = unsigned long long;
const i64 iinf = 0x3f3f3f3f, linf = 0x3f3f3f3f3f3f3f3f;
const int N = 510, mod = 1e9 + 7;
i64 n, tot, ans;
struct node {
	i64 l, r;
} a[N];
i64 b[N << 1], f[N][N << 1], inv[N], l[N << 1];
int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);
    
	std::cin >> n;
	for(int i = 1; i <= n; i++) {
		std::cin >> a[i].l >> a[i].r;
		b[++tot] = a[i].l, b[++tot] = ++a[i].r;
	}
	std::sort(b + 1, b + tot + 1);
	tot = std::unique(b + 1, b + tot + 1) - b - 1;
	
	for(int i = 1; i <= n; i++) {
		a[i].l = std::lower_bound(b + 1, b + tot + 1, a[i].l) - b;
		a[i].r = std::lower_bound(b + 1, b + tot + 1, a[i].r) - b;
	}

	tot--;
	for(int i = 1; i <= tot; i++) {
		l[i] = b[i + 1] - b[i];
	}
	inv[1] = 1;
	for(int i = 2; i <= n; i++) {
		inv[i] = 1LL * (mod - mod / i) * inv[mod % i] % mod;
	}

	for(int i = 0; i <= tot; i++) f[0][i] = 1;
	for(int i = 1; i <= n; i++) {
		for(int j = a[i].l, r = a[i].r; j < r; j++) {
			i64 C = l[j], len = l[j], m = 1;
			for(int k = i - 1; k >= 0; k--) {
				f[i][j] = (f[i][j] + 1LL * f[k][j - 1] * C % mod) % mod;
				if(a[k].l <= j && j < a[k].r) {
					len++, m++;
					C = (C * len % mod * inv[m] % mod) % mod;
				}
			}
		}
		for(int j = 2; j <= tot; j++) f[i][j] = (f[i][j] + f[i][j - 1]) % mod;
	}
	
	for(int i = 1; i <= n; i++) {	
		ans = (ans + f[i][tot]) % mod;
	}
	std::cout << ans << "\n";

	return 0;
}
posted @   Fire_Raku  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
点击右上角即可分享
微信分享提示