划艇:dp/组合数/区间离散化
Description
在首尔城中,汉江横贯东西。在汉江的北岸,从西向东星星点点地分布着 N 个划艇学校,编号依次为 1 到 N。每个学校都拥有若干艘划艇。同一所学校的所有划艇颜色相同,不同的学校的划艇颜色互不相同。
颜色相同的划艇被认为是一样的。每个学校可以选择派出一些划艇参加节日的庆典,也可以选择不派出任何划艇参加。如果编号为 i的学校选择派出划艇参加庆典,那么,派出的划艇数量可以在ai到bi之间选择 至 bib_ibi 之间任意选择(ai≤bi)。
值得注意的是,编号为i 的学校如果选择派出划艇参加庆典,那么它派出的划艇数量必须大于任意一所编号小于它的学校派出的划艇数量。
输入所有学校的 ai,bii,bi 的值,求出参加庆典的划艇有多少种可能的情况,必须有至少一艘划艇参加庆典。两种情况不同当且仅当有参加庆典的某种颜色的划艇数量不同。
Input
第一行包括一个整数 N,表示学校的数量。
接下来 N 行,每行包括两个正整数,用来描述一所学校。其中第 iii 行包括的两个正整数分别表示 ai,bi(1≤ai≤bi≤1e9)i,bi(1≤ai≤bi≤109)。
Output
输出一行,一个整数,表示所有可能的派出划艇的方案数除以 1e9+79+7 得到的余数。
Sample Input
2 1 2 2 3
Sample Output
7
Hint
在只有一所学校派出划艇的情况下有 4 种方案,两所学校都派出划艇的情况下有 3 种方案,所以答案为 7。
子任务 1(9 分):1≤N≤500 且对于所有的 1≤i≤N1 \le i \le N1≤i≤N,保证 ai=bia_i=b_iai=bi。
子任务 2(22 分):1≤N≤500 且 ∑i=1-N(bi−ai)≤106i=1N(bi−ai)≤106。
子任务 3(27 分):1≤N≤100。
子任务 4(42 分):1≤N≤500。
高难标记
首先作为考试题还是需要提一下部分分的
9%算法:类似于拦截导弹,dp
31%算法:动态开点线段树,每次只枚举a和b之间的区间,维护前缀和
100%算法:
网上的题解都是坑人的!
首先看到数据范围这么大,一定需要离散化。
输入中出现的不同值最多一共有2n种,可以离散。
那么现在假如我们已经成功的离散化完毕。
原来的dp[i][j]表示最后一所派出游艇的学校是i,第i所学校派出了j个划艇
现在既然点数变少了,那么j不能再只表示j这个单点了
假如在出现的那2n个数去重离散化后第j小的数是num[j],第j+1小的数是num[j+1]
那么我们用dp[i][j]表示最后一所派出游艇的学校是i,第i所学校派出划艇的数量在[num[j],num[j+1]),注意是左闭右开那么其实左开右闭也可以。。但一定要是半开半闭的,否则就有重复区间了
那么接下来,对于每个学校,我们需要用新的这些零散的区间拼出原区间,但是因为它是左闭右开的,所以不能恰好拼出来
我们可以在输入b[i]时将其+1,并不是真的变大了,而是让离散化后的区间恰好能拼出原区间(其实就是让原区间也变成了左闭右开)
如:输入[3,11],[6,15],我们可以将其转化为[3,12),[6,16),离散化3,6,12,16四个数。
得到的区间是[3,6),[6,12),[12,16),[16,无穷大)。表示原区间:[3,12)=[3,6)+[6,12)就很简单了
接下来考虑怎么状态转移:一共两种情况
1)上一个派出游艇的学校派出的游艇数量和本学校派出的不在同一个区间
dp[i][j]+=∑ii=1->i-1∑jj=i->j-1 dp[ii][jj]
显然+=后面的那个东西是dp数值表的一个整块,我们可以处理二维前缀和,用sum表示
dp[i][j]+=sum[i-1][j-1]; sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+dp[i][j];
后者是一个小容斥,前两个矩形的重叠部分减去就可以了,画个图就明白了
2)上一个派出游艇的学校派出的游艇数量和本学校派出的在同一个区间
为了方便,我们枚举出现在同一区间的编号最小的学校,设之为k
那么在k到i之间的所有学校,要么也出现在这个区间,要么干脆没有派出划艇
我们求出k~i这一段的方案数,乘以k以前的方案数sum[k-1][j-1]即可
那么显然,对于k~i这一段里的学校,如果它派出的划艇数量不能在j对应的这个区间内
那么它一定不能派出游艇(在第k和第i个学校都派出了j区间内游艇的前提下)。
所以我们只需要考虑k~i之间能派出j个划艇的学校,设这样的学校数量为can(含i和k)
那么就有如下子问题:
有can个相同的区间j,从每个区间中可以选数也可以不选,要求所有选出的数严格递增,且第一个和最后一个区间必选,求方案数?
假如我们一共选择了p个(p>=2因为i和k必选),区间j中一共有len个不同的数
分两步:从len个数中选出p个从小到大排序,再从i~k中能派出划艇的can个学校中选出p-2个要派出划艇的学校、
(将选出的p个数依次赋予p个选中的学校,即那p-2个加上i和k,每个学校被赋予的数字就是它派出的数量)
对于每一种合理的分数字的方案,都是一种派划艇的方案
那么选择p个学校时的方案数是C(len,p)*C(can,p-2) = C(len,len-p)*C(can,p-2)
全部方案就是∑p=2->can C(len,len-p)*C(can,p-2)
其实p也可以从0开始枚举,因为那时C(can,p-2)=0,不会对答案产生影响
为了方便,就当作从0开始吧
可以发现两个组合数中的len-p与p-2的和是定值,考虑它的含义
你有l个物品,我有can个,你要从你的里选出len-p个,我要从我的里选出p-2个
把咱们的物品放在一起,一共can+len个,从里面随便选出len-p+p-2=len-2个不就好了么?
所以那个和式,其实就是C(can+len,len-2)=C(can+len,can+2)
所以对dp[i][j]的贡献就是∑k=1->i-1C(can+len,can+2)*sum[k-1][j-1](k可以派出j区间内的划艇数)
如果我们能O(1)计算那个组合数,那么就可以O(n3)解决了
考虑怎么计算它。我们可以发现当枚举k时,如果k不能派出j之间的划艇数,就跳过
否则,can++。反应一下,can每次只增加了1。
C(n,m)= n! / m! / (n-m)!
C(n+1,m+1)= (n+1)! /(m+1)! / (n-m)! = n! / m! / (n-m)! *(n+1)/(m+1)
所以我们只需要把逆元搞出来,组合数就可以O(1)递推了
初状态是C(len-2,0),不计入答案,随着++can,c*=(can+l-2)*inv[can]
其实也可以不必这么麻烦,组合数貌似是可以杨辉三角的。
注意一下:在枚举j的时候,因为左开右闭的性质,是从a[i]~b[i]-1而非a[i]~b[i]
提示:推荐#define int long long,既不怕取模出问题,而且能加速。
至于为什么。。。过。
还有什么问题么?评论区吧。
const int kx = ;
// Daily Orz Rank #1
// Orz Rank #1
// Rank #1
//#1
#include <cstdio>
#include <map>
#include <algorithm>
using namespace std;
#define int long long
map<int, int> m;
int ref[], a[], b[], n, cnt, res[], dp[][], sum[][], len[], inv[];
int pow(long long a, int t = kx - , long long ans = ) {
for (; t; t >>= , (a *= a) %= kx)
if (t & )
(ans *= a) %= kx;
return ans;
}
main() {
scanf("%lld", &n);
for (int i = ; i <= n; ++i)
scanf("%lld%lld", &a[i], &b[i]), b[i]++, res[(i << ) - ] = a[i], res[i << ] = b[i];
sort(res + , res + + (n << ));
for (int i = ; i <= (n << ); ++i)
if (res[i] != res[i - ])
ref[++cnt] = res[i], m[res[i]] = cnt;
for (int i = ; i <= n; ++i) a[i] = m[a[i]], b[i] = m[b[i]];
inv[] = inv[] = ;
dp[][] = sum[][] = ;
res[cnt + ] = res[cnt] + ;
for (int i = ; i <= cnt; ++i) len[i] = ref[i + ] - ref[i];
for (int i = ; i <= ; ++i) inv[i] = (kx - 1ll * kx / i * inv[kx % i] % kx) % kx;
for (int i = ; i <= n; ++i) sum[i][] = ;
for (int i = ; i <= cnt; ++i) sum[][i] = ;
for (int i = ; i <= n; ++i) {
for (int j = ; j < a[i]; ++j)
sum[i][j] = (sum[i - ][j] + sum[i][j - ] - sum[i - ][j - ] + dp[i][j]) % kx;
for (int j = a[i]; j < b[i]; ++j) {
dp[i][j] = 1ll * sum[i - ][j - ] * len[j] % kx; // printf("%lld %lld %lld\n",i,j,dp[i][j]);
register int c = len[j] - , can = , l = len[j];
for (int k = i - ; k; --k)
if (a[k] <= j && j < b[k])
++can, (c *= (can + l - ) * inv[can] % kx) %= kx,
(dp[i][j] += 1ll * sum[k - ][j - ] * (c)) %=
kx; //, printf("%lld %lld %lld %lld %lld\n",i,j,k,(c),sum[k-1][j-1]);
sum[i][j] = ((sum[i - ][j] + sum[i][j - ]) % kx - sum[i - ][j - ] + dp[i][j] + kx) % kx;
}
for (int j = b[i]; j <= cnt; ++j)
sum[i][j] = ((sum[i - ][j] + sum[i][j - ]) % kx - sum[i - ][j - ] + dp[i][j] + kx) % kx;
}
// for(int i=1;i<=n;++i){for(int j=1;j<=cnt;++j)printf("%lld ",sum[i][j]);puts("");}puts("");
// for(int i=1;i<=n;++i){for(int j=1;j<=cnt;++j)printf("%lld ",dp[i][j]);puts("");}
printf("%lld\n", sum[n][cnt] - );
}
感谢loj码风优化
划艇:dp/组合数/区间离散化的更多相关文章
- 【区间dp+组合数+数学期望】Expression
https://www.bnuoj.com/v3/contest_show.php?cid=9148#problem/I [题意] 给定n个操作数和n-1个操作符,组成一个数学式子.每次可以选择两个相 ...
- Contest 20140708 testB dp 组合数
testB 输入文件: testB.in 输出文件testB.out 时限3000ms 问题描述: 定义这样一个序列(a1,b1),(a2,b2),…,(ak,bk)如果这个序列是方序列的话必须满足 ...
- POJ 2528 Mayor's posters 【区间离散化+线段树区间更新&&查询变形】
任意门:http://poj.org/problem?id=2528 Mayor's posters Time Limit: 1000MS Memory Limit: 65536K Total S ...
- noj 2033 一页书的书 [ dp + 组合数 ]
传送门 一页书的书 时间限制(普通/Java) : 1000 MS/ 3000 MS 运行内存限制 : 65536 KByte总提交 : 53 测试通过 : 1 ...
- 线段树区间离散化维护按秩合并并查集(可撤销)——牛客多校第八场E
模板题..去网上学了可撤销的并查集.. /* 给定一个无向图,边的属性为(u,v,l,r),表示<u,v>可以通过的size为[l,r] 求出有多少不同的size可以从1->n 把每 ...
- 线段树区间离散化——牛客多校E
这个区间离散化把我调死了.. 总之用vector来离散化,然后叶子节点维护的是一段区间,记录下每个叶子结点的起点+长度 千万要注意下标不能弄错! #include<bits/stdc++.h&g ...
- NOI2016区间bzoj4653(线段树,尺取法,区间离散化)
题目描述 在数轴上有 \(N\) 个闭区间 \([l_1,r_1],[l_2,r_2],...,[l_n,r_n]\) .现在要从中选出 \(M\) 个区间,使得这 \(M\) 个区间共同包含至少一个 ...
- HDU 5396 Expression(DP+组合数)(详解)
题目大意: 给你一个n然后是n个数. 然后是n-1个操作符,操作符是插入在两个数字之间的. 由于你不同的运算顺序,会产生不同的结果. 比如: 1 + 1 * 2 有两种 (1+1)*2 或者 ...
- BZOJ 1852 [MexicoOI06]最长不下降序列(贪心+DP+线段树+离散化)
[题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1852 [题目大意] 给你N对数A1,B1……An,Bn.要求你从中找出最多的对, 把它 ...
随机推荐
- Akka实现WordCount(Scala)
Akka实现WordCount(Scala): 架构图: 项目结构: pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0 ...
- IDEA统一注释格式(干货)
统一注释格式: Idea自动生成注释: /** *@author 你的名字的简写 *@date ${DATE}${TIME} 在每个功能上添加注释,例子如下: 我在Mapper文件中添加了自己书写的S ...
- 【Java 基础】谈谈集合.List
目录 1. ArrayList 1.1 ArrayList的构造 1.2 add方法 1.3 remove方法 1.4 查询方法 1.5 一些其他常用方法 1.6 ArrayList小结 2. Vec ...
- 渗透测试-基于白名单执行payload--Cmstp
0x01 Cmstp简介 Cmstp安装或删除“连接管理器”服务配置文件.如果不含可选参数的情况下使用,则 cmstp 会使用对应于操作系统和用户的权限的默认设置来安装服务配置文件. 微软官方文档: ...
- Tree 点分治
题目描述 给你一棵TREE,以及这棵树上边的距离.问有多少对点它们两者间的距离小于等于K 输入输出格式 输入格式: N(n<=40000) 接下来n-1行边描述管道,按照题目中写的输入 接下来是 ...
- Java代码性能优化的 39个细节
在JAVA程序中,性能问题的大部分原因并不在于JAVA语言,而是程序本身.养成良好的编码习惯非常重要,能够显著地提升程序性能. 1:在合适的场合使用单例 使用单例可以减轻加载的负担,缩短加载的时间,提 ...
- StreamWriter 相关知识分享
在介绍StreamWriter之前,我们首先来了解一下它的父类TextWriter. 一.TextWriter 1.TextWriter的构造函数和常用属性方法 下面是TextWriter的构造函数: ...
- 《HTML5+CSS3+JavaScript 从入门到精通(标准版)》学习笔记(二)
这是一个应用的例子,学以致用嘛 <!--这些代码我就直接放在了博客园的"页首Html代码"中,用于自定义博客,效果就是页面左上角的白色文字--> <p> & ...
- 什么是Kafka?
1 kafka 是什么 Apache kafka is a distributed streaming platform,即官方定义 kafka 是一个分布式流式计算平台.而在大部分企业开发人员中,都 ...
- javascript单线程,异步与执行机制
js的单线程模型与游览器的进程/线程息息相关,在了解js单线程与异步的时候,建议先看看这篇文章 为什么是单线程 由于js是可操作dom的,如果js是多线程,在多线程的交互下,处于界面中的dom节点就可 ...