计数难题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_{i,j}\) 表示考虑完前\(i\)个区间,当前区间必须选一个元素,且这个元素在第\(j\)段的方案数。
转移的时候,暴力枚举上一个不在同一区间的元素选在哪个区间\(k\)。
设\([k+1,i]\)范围内能够在第\(j\)段选数的区间个数为\(m\),设当前这段的长度为\(len\)。
我们再暴力枚举这\(m\)个元素中有\(l\)个元素在当前这段中选了。
那么就可以得到转移方程:
\[f_{i,j} = \sum_{k=0}^{i-1} (\sum_{t=1}^{j-1} f_{k,t})(\sum_{l=1}^{min(m,len)} \binom{m-1}{l-1}\binom{len}{l})\]
显然\(\sum_{t=1}^{j-1} f_{k,t}\) 是可以记前缀和的。
所以我们现在的问题变为求\(\sum_{l=1}^{min(m,len)} \binom{m-1}{l-1}\binom{len}{l}\)。
我们有\(\sum_{l=1}^{min(m,len)} \binom{m-1}{l-1}\binom{len}{l} = \sum_{l=1}^{min(m,len)} \binom{m-1}{m-l}\binom{len}{l}\)
所以可以用范德蒙恒等式化简:

\[\sum_{l=1}^{min(m,len)} \binom{m-1}{m-l}\binom{len}{l} = \binom{m+len-1}{m}\]
现在的转移方程就变为了:
\[f_{i,j} = \sum_{k=0}^{i-1} \binom{m+len-1}{m} (\sum_{t=1}^{j-1} f_{k,t})\]

唯一的问题就是如何求\(\binom{m+len-1}{m}\)了,因为\(len\)可能达到\(10^9\)级别。
注意到组合数的改变只与\(m\)有关。
所以组合数的变化是上下同时\(+1\)。
所以可以套用吸收-归纳恒等式:\(\binom{n+1}{m+1} = \frac{n+1}{m+1} \binom{n}{m}\)。
我们枚举\(k\),然后在转移前先预处理好组合数即可。

实现代码

#include<bits/stdc++.h>
#define IL inline
#define _ 1015
#define ll long long
using namespace std ;

IL int gi(){
    int data = 0 , m = 1; char ch = 0;
    while(ch!='-' && (ch<'0'||ch>'9')) ch = getchar();
    if(ch == '-'){m = 0 ; ch = getchar() ; }
    while(ch >= '0' && ch <= '9'){data = (data<<1) + (data<<3) + (ch^48) ; ch = getchar(); }
    return (m) ? data : -data ;
}

#define mod 1000000007

int f[_][_] , g[_][_] ;
int Ans ;
int n,m,L[_],R[_],X[_],A[_],B[_],inv[_],len[_],Comb[_] ;

int main() {
    n = gi() ;
    for(int i = 1; i <= n; i ++) L[i] = gi() , R[i] = gi() ;
    for(int i = 1; i <= n; i ++) X[++m] = L[i] , X[++m] = R[i] + 1 ;
    X[++m] = 0 ;
    sort(X + 1 , X + m + 1) ;
    X[m+1] = X[m] + 1 ; ++ m ;
    m = unique(X + 1 , X + m + 1) - X - 1 ;
    for(int i = 1; i <= m; i ++) len[i] = (X[i + 1] - X[i]) % mod ;
    for(int i = 1; i <= n; i ++) {
        A[i] = lower_bound(X + 1 , X + m + 1 , L[i]) - X ;
        B[i] = upper_bound(X + 1 , X + m + 1 , R[i]) - X - 1 ;
    }
    -- m ;
    inv[0] = inv[1] = 1 ;
    for(int i = 2; i <= n + 1; i ++) inv[i] = 1ll * (mod-mod/i) * inv[mod%i] % mod ;
    f[0][0] = 1 ;
    g[0][0] = f[0][0] ;
    for(int j = 1; j <= m; j ++) g[0][j] = (f[0][j] + g[0][j - 1]) % mod ;
    for(int i = 1,a,b; i <= n; i ++) {
        for(int j = A[i]; j <= B[i]; j ++) {
            a = (len[j] - 1) % mod ; b = 0 ;
            Comb[i] = 1 ;
            for(int k = i - 1; k >= 0; k --) {
                if(A[k + 1] <= j && j <= B[k + 1]) {
                    ++ a ; ++ b ;
                    Comb[k] = 1ll * a * Comb[k + 1] % mod * inv[b] % mod ;
                }
                else Comb[k] = Comb[k + 1] ;
            }
            for(int k = i - 1; k >= 0; k --) f[i][j] = (f[i][j] + 1ll * Comb[k] * g[k][j-1] % mod) % mod ;
        }
        g[i][0] = f[i][0] ;
        for(int j = 1; j <= m; j ++) g[i][j] = (f[i][j] + g[i][j - 1]) % mod ;
        Ans = (Ans + g[i][m]) % mod ;
    }
    cout << Ans % mod << endl ;
    return 0 ;
}

原文地址:https://www.cnblogs.com/GuessYCB/p/9903606.html

时间: 2024-10-09 06:43:07

计数难题5:[APIO2016]划艇的相关文章

计数难题3:LuoguT46780 妹子序列

计数难题3:LuoguT46780 妹子序列 标签(空格分隔): 计数难题题选 题目大意: 丢个网址:戳我(QwQ) 给定\(n\).\(m\). 求\(n\)的所有排列中,逆序对个数为\(m\)的排列个数.数据范围:\(n,m\leq 10^5\) . 题解 朴素\(dp\):\(dp_{i,j}\)表示放完\(i\),有\(j\)个逆序对的方案数. 转移太简单了:\(dp_{i,j} = \sum_{k=0}^{i-1} dp_{i-1,k}\) ,复杂度\(O(n^2)\)美滋滋. 注意到

[APIO2016]划艇

题目描述 在首尔城中,汉江横贯东西.在汉江的北岸,从西向东星星点点地分布着 NNN 个划艇学校,编号依次为 111 到 NNN.每个学校都拥有若干艘划艇.同一所学校的所有划艇颜色相同,不同的学校的划艇颜色互不相同.颜色相同的划艇被认为是一样的.每个学校可以选择派出一些划艇参加节日的庆典,也可以选择不派出任何划艇参加.如果编号为 iii 的学校选择派出划艇参加庆典,那么,派出的划艇数量可以在 aia_ia?i?? 至 bib_ib?i?? 之间任意选择(ai≤bi). 值得注意的是,编号为 iii

[Codeforces 1295F]Good Contest(DP+组合数学)

[Codeforces 1295F]Good Contest(DP+组合数学) 题面 有一个长度为\(n\)的整数序列,第\(i\)个数的值在\([l_i,r_i]\)中随机产生.问这个序列是一个不上升序列的概率(模\(998244353\)意义下). \(n \leq 50,l_i,r_i \leq 998244351\) 分析 和[APIO2016]划艇几乎一模一样.可惜比赛的时候时间不够. 首先把问题转化成求最长不上升序列的数量. 我们把这些区间离散化,分割成两两之间不互相覆盖的若干个区间

【loj2567】【APIO2016】划艇

题目 \(N\)个位置,每个位置要么不选,要么选\([ a_i, b_i ]\)中的一个数: 问最后的单调上升序列(mod 1e9+7)有多少种: \(1 \le N \le 500\) 题解 orz abclzr 直接\(dp\)最后一位是什么数字的话只能得到31分 将数字离散化分段,第\(i\)段为\([l_i,r_i)\),设\(f_{i,j}\)表示第i个位置选的数字在第j段的方案数(第0段表示没有) \[ f_{i,j} \ = \sum_{k=0}^{i-1} \sum_{l=0}^

bzoj 2401: 陶陶的难题I 数论

2401: 陶陶的难题I Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 89  Solved: 24[Submit][Status] Description 最近陶陶在研究数论,某天他偶然遇到一道题:对于给定的正整数???,求出 下面这样一个式子的值: 其中LCM(a???, b???)表示正整数???和???最小公倍数,即能同时被a???和b???整除的最小正 整数. 作为神犇的陶陶,当然轻松秒杀了这道题.不过他希望你写一个程序,用来 检验他算

1123: 统计难题 (字典树)

1123: 统计难题 时间限制: 1 Sec  内存限制: 128 MB 提交: 4  解决: 4 [提交][状态][讨论版] 题目描述 Ignatius最近遇到一个难题,老师交给他很多单词(只有小写字母组成,不会有重复的单词出现),现在老师要他统计出以某个字符串为前缀的单词数量(单词本身也是自己的前缀). 输入 输入数据的第一部分是一张单词表,每行一个单词,单词的长度不超过10,它们代表的是老师交给Ignatius统计的单词,一个空行代表单词表的结束.第二部分是一连串的提问,每行一个提问,每个

为什么计数要从零开始

网址:http://blog.jobbole.com/95826/   为什么应该从 0 开始计数? 要避免使用可恶的省略号来表示自然数子序列 2. 3. …….12,通常有四种表达方式: a) 2 ≤ i < 13 b) 1 < i ≤ 12 c) 2 ≤ i ≤ 12 d) 1 < i < 13 有理由证明其中的一种比其它的更好吗?答案是肯定的.根据观察表达方式, a) 和 b) 更有优势,因为它们边界的差值,刚好和子序列的长度一致.所以观察结果说明,采用上述两种表达方式的两个

hdu 1251 统计难题 字典树

// hdu 1251 统计难题 字典树 // // 题目大意: // // 有一系列的单词表,以空行结尾,之后会有一些字母串,找出以这些字符串 // 作为前缀的单词的个数 // // // 解题思路: // // 字典树 Trie,在插入字符串的时候每遇到一个节点,该节点的值++.查找的时候 // 字符串时,如果找到了,那么返回当前的val,否则返回0,因为没有以这个字符串 // 为前缀的单词. // // // 感悟: // // 这段时间想学学数据结构,就看了看刘老的大白书,感觉用数组挺巧

1225 八数码难题

1225 八数码难题 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题解 查看运行结果 题目描述 Description Yours和zero在研究A*启发式算法.拿到一道经典的A*问题,但是他们不会做,请你帮他们.问题描述 在3×3的棋盘上,摆有八个棋子,每个棋子上标有1至8的某一数字.棋盘中留有一个空格,空格用0来表示.空格周围的棋子可以移到空格中.要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为123804765