• LOJ 2567: 洛谷 P3643: bzoj 4584: 「APIO2016」划艇


    题目传送门:LOJ #2567

    题意简述:

    (n) 个位置,第 (i) 个位置可以填在 ([a_i,b_i])(1le a_ile b_ile 10^9))之间的整数,也可以填 (0)

    如果第 (i) 个位置填了非 (0) 的数,则这个数必须大于之前所有位置((1)(i-1) 的位置)上的数。

    至少要有一个位置填上非 (0) 的数。问最终有几种填数方案,两种填数方案不同当且仅当某个位置上填的数不同。

    题解:

    要求即为选出一些位置填数,并且形成严格递增序列。

    发现把所有区间离散化,可以组成 (mathcal{O}(n)) 个连续段,每个连续段中的数的性质是相同的。

    因为 (n) 不大(只有 (500)),考虑 DP。令 (mathrm{f}[i][j]) 表示前 (i) 个位置,第 (i) 个位置填了在段 (j) 中的数的方案数。

    显然若 ([a_i,b_i]) 不包含段 (j),则 (mathrm{f}[i][j]=0)。对于其他的情况,考虑如何转移,假设上一个填了在小于 (j) 的段中的数的位置是 (k),则有 (mathrm{f}[i][j])(mathrm{f}[k][j']) 转移,其中 (0le k<i)(1le j'<j)

    转移的具体形式是:假设位置 ((k,i]) 中一共有 (pos) 个位置(包括 (i) 本身)包含段 (j),而段 (j) 的长度为 (len),则 (mathrm{f}[k][j']) 贡献 (displaystylesum_{x=1}^{pos}inom{pos-1}{x-1}inom{len}{x}) 倍给 (mathrm{f}[i][j])。这是因为我们枚举 (pos) 个位置中填了 (x) 个位置,但是第 (i) 个位置必须填,所以乘上 (displaystyleinom{pos-1}{x-1}),然后 (len) 个可行数中选出 (x) 个从小到大依次填入,所以再乘上 (displaystyleinom{len}{x})。化简一下系数:(displaystylesum_{x=1}^{pos}inom{pos-1}{x-1}inom{len}{x}=sum_{x}inom{len}{x}inom{pos-1}{pos-x}=inom{len+pos-1}{pos})。式子可以这样理解:枚举 (x),从 (len) 个数中选出 (x) 个数,再从 (pos-1) 个数中选出 (pos-x) 个数,这与直接从 (len+pos-1) 个数中选出 (pos) 个数等价。

    那么我们有 (displaystylemathrm{f}[i][j]=sum_{k=0}^{i-1}inom{len+pos-1}{pos}sum_{j'=0}^{j-1}mathrm{f}[k][j'])。边界是 (mathrm{f}[0][0]=1)(mathrm{f}[0][j]=0)(1le j))和 (mathrm{f}[i][0]=0)(1le i))。

    再利用前缀和优化,把第二维做前缀和,把转移变成 (mathcal{O}(n)) 的就好了,转移内的组合数可以 (mathcal{O}(n)) 预处理。

    还可以滚动数组一下,空间就是 (mathcal{O}(n)) 的了,不过没必要。

    #include <cstdio>
    #include <algorithm>
    
    typedef long long LL;
    const int Mod = 1000000007;
    const int MN = 505;
    
    int N, lb[MN], rb[MN], lp[MN * 2], len[MN * 2], cnt;
    int Inv[MN], C[MN], f[MN], Ans;
    
    int main() {
    	scanf("%d", &N);
    	for (int i = 1; i <= N; ++i)
    		scanf("%d%d", &lb[i], &rb[i]),
    		lp[++cnt] = lb[i],
    		lp[++cnt] = rb[i] + 1;
    	std::sort(lp + 1, lp + cnt + 1);
    	cnt = std::unique(lp + 1, lp + cnt + 1) - lp - 2;
    	for (int i = 1; i <= cnt; ++i) len[i] = lp[i + 1] - lp[i];
    	for (int i = 1; i <= N; ++i)
    		lb[i] = std::lower_bound(lp + 1, lp + cnt + 1, lb[i]) - lp,
    		rb[i] = std::lower_bound(lp + 1, lp + cnt + 1, rb[i] + 1) - lp - 1;
    	Inv[1] = 1, C[0] = 1, f[0] = 1;
    	for (int i = 2; i <= N; ++i) Inv[i] = (LL)(Mod - Mod / i) * Inv[Mod % i] % Mod;
    	for (int i = 1; i <= cnt; ++i) {
    		int l = len[i];
    		for (int j = 1; j <= N; ++j)
    			C[j] = (LL)C[j - 1] * (l + j - 1) % Mod * Inv[j] % Mod;
    		for (int j = N; j >= 1; --j) if (lb[j] <= i && i <= rb[j]) {
    			for (int k = j, a = 0; k >= 1; --k) {
    				if (lb[k] <= i && i <= rb[k]) ++a;
    				f[j] = (f[j] + (LL)f[k - 1] * C[a]) % Mod;
    			}
    		}
    	}
    	for (int i = 1; i <= N; ++i) Ans = (Ans + f[i]) % Mod;
    	printf("%d
    ", Ans);
    	return 0;
    }
    
  • 相关阅读:
    django form表单验证
    Django messages框架
    pymysql 操作数据库
    python数据类型详解及列表字典集合推导式详解
    深入flask中的request
    修改sqlarchemy源码使其支持jdbc连接mysql
    深入理解Python中协程的应用机制: 使用纯Python来实现一个操作系统吧!!
    svg坐标转换
    近几年总结
    frp中的json模块
  • 原文地址:https://www.cnblogs.com/PinkRabbit/p/10502307.html
Copyright © 2020-2023  润新知