划艇: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.要求你从中找出最多的对, 把它 ...
随机推荐
- Solidity 编程实例--投票
Voting 投票 思路是为每张选票创建一个合约,每个投票选项提供一个短名称.合约创建者作为会长将会给每个投票参与人各自的地址投票权. 地址后面的人们可以选择自己投票或者委托信任的代表人替他们投票.在 ...
- Activity的四种加载模式详解:
先来看看总结图: 模式详解: standard模式: 标准启动模式,也是activity的默认启动模式.在这种模式下启动的activity可以被多次实例化,即在同一个任务中可以存在多个activity ...
- centos8安装图解
CentOS 8 的新特性 DNF 成为了默认的软件包管理器,同时 yum 仍然是可用的 使用网络管理器(nmcli 和 nmtui)进行网络配置,移除了网络脚本 使用 Podman 进行容器管理 引 ...
- 洛谷 P3745 [六省联考2017]期末考试
题目描述 有 nnn 位同学,每位同学都参加了全部的 mmm 门课程的期末考试,都在焦急的等待成绩的公布. 第 iii 位同学希望在第 tit_iti 天或之前得知所有课程的成绩.如果在第 tit_ ...
- Mac安装Command Line Tools
从App Store上下载的Xcode,默认是不会安装Command Line Tools的,Command Line Tools是在Xcode中的一款工具,可以在命令行中运行C程序. 在终端中输入命 ...
- 本人亲测-SSM环境搭建(使用eclipse作为示例,过程挺全的,可作为参考)
本人亲测-SSM环境搭建(使用eclipse作为示例,过程挺全的,可作为参考) 本人亲测-SSM环境搭建(使用eclipse作为示例,过程挺全的,可作为参考) 本人亲测-SSM环境搭建(使用eclip ...
- 配置Ant执行Jmeter脚本
1.将 jmeter下extras目录中ant-jmeter-1.1.1.jar包拷贝至ant安装目录下的lib目录中,否则会报错ant-jmeter-1.1.1不存在 2.在jmeter根目录下创建 ...
- Intel Ivy Bridge Microarchitecture Events
This is a list of all Intel Ivy Bridge Microarchitecture performance counter event types. Please see ...
- 在Ubuntu16.0.4安装hipcaffe
1. 安装 AMD ROCm 显卡条件 要安装AMD的 ROCm显卡,必须满足以下条件,只能高于下面信息版本,不能低于. Distribution Kernel GCC GLIBC x86_64 Fe ...
- Maven安装和在IDEA配置Maven
一.Windows安装Maven 1.下载Maven 这里需要注意:不要去官网下载最新的版本,因为会出现与IDEA不兼容的现象. 这里提供下载地址:https://archive.apache.org ...