[洛谷P3643] [APIO2016]划艇
洛谷题目链接:[APIO2016]划艇
题目描述
在首尔城中,汉江横贯东西。在汉江的北岸,从西向东星星点点地分布着 N 个划艇学校,编号依次为 1 到 N。每个学校都拥有若干艘划艇。同一所学校的所有划艇颜色相同,不同的学校的划艇颜色互不相同。颜色相同的划艇被认为是一样的。每个学校可以选择派出一些划艇参加节日的庆典,也可以选择不派出任何划艇参加。如果编号为 iii 的学校选择派出划艇参加庆典,那么,派出的划艇数量可以在 ai 至 bi 之间任意选择(ai≤bi)。
值得注意的是,编号为 i 的学校如果选择派出划艇参加庆典,那么它派出的划艇数量必须大于任意一所编号小于它的学校派出的划艇数量。
输入所有学校的 ai,bi 的值,求出参加庆典的划艇有多少种可能的情况,必须有至少一艘划艇参加庆典。两种情况不同当且仅当有参加庆典的某种颜色的划艇数量不同。
输入输出格式
输入格式:
第一行包括一个整数 N,表示学校的数量。
接下来 N 行,每行包括两个正整数,用来描述一所学校。其中第 i 行包括的两个正整数分别表示 ai,bi(1≤ai≤bi≤109)。
输出格式:
输出一行,一个整数,表示所有可能的派出划艇的方案数除以 1,000,000,007 得到的余数。
输入输出样例
输入样例#1:
2
1 2
2 3
输出样例#1:
7
说明
【样例解释】
在只有一所学校派出划艇的情况下有 4 种方案,两所学校都派出划艇的情况下有 3 种方案,所以答案为 7。
【数据范围】
子任务 1(9 分):1≤N≤500 且对于所有的 1≤i≤N,保证 ai=bi。
子任务 2(22 分):1≤N≤500 且 ∑Ni=1(bi−ai)≤106。
子任务 3(27 分):1≤N≤100。
子任务 4(42 分):1≤N≤500。
题解: 首先来考虑这样一个问题:如果有3个相同的区间,每个区间可以选数也可以不选,但是每个区间最多只能取出一个数,区间内的数属于[1,len],要求取出其中的值严格递增,方案数有多少种?
我们可以考虑用加法原理枚举一下每种情况:
- 一个区间都不选:C03∗C0len=1
- 只有一个区间取数:C13∗C1len=3∗len
- 有两个区间取数:C23∗C2len=3∗len∗(len−1)2
- 三个区间都取数:C33∗C3len=1∗len∗(len−1)∗(len−2)6
加起来就是$$C_{3}^{0} \times C_{len}^{0} + C_{3}^{1} \times C_{len}^{1} + C_{3}^{2} \times C_{len}^{2} + C_{3}^{3} \times C_{len}{3}因为$Cmn=Cn−mn$,式子也可以表示成:C_{3} \times C_{len}^{0} + C_{3}^{2} \times C_{len}^{1} + C_{3}^{1} \times C_{len}^{2} + C_{3}^{0} \times C_{len}^{3}$$
考虑一下这个式子的实际意义:将一个长度为len+3的区间劈成两半,两边总共选出3个数,也就相当于:C3len+3
我们把这个结论扩展一下:有n个相同的区间,区间内的数的范围是[1,len],每个区间可以选择取或不取数,但是一个区间最多只能取一个数,方案数为Cnlen+n
好的,那么这个和我们的题目有什么关系呢?
首先我们将划艇的数量离散,用tmpi记录排名为i的值,我们设区间i表示[tmpi,tmpi+1)那么某个学校派出的划艇的数量一定是包含在一个或多个区间内的.设f[i][j]表示当前处理到第i个学校,且第i个学校派出的划艇数量在j区间内的方案数.那么如果某个学校派出的划艇数量与第i个学校派出的划艇数量没有交集,那么就可以直接加上这部分的方案数:lenj×k−1∑i′=0j−1∑j′=0f[i′][j′]
然后我们需要考虑枚举在[0,i−1]中与学校i的派出人数有交集的学校,将交集这一部分的贡献用组合的方法算出来.假设现在已经扫到了学校k(k∈[0,i−1]),k与i有交集,且k学校必须派出划艇,那么根据上面推出的结论,这一部分的贡献为:i−1∑i′=1Ci−i′−1i−i′+len−1×i′−1∑k′=1j−1∑j′=1f[k][j]
然后,我们会发现直接这样算是O(n5)的,但是我们又会发现前面枚举f[i′][j′]可以直接前缀和,那么时间复杂度又降到了O(n3).
再讲一下初始化的问题,因为f[0][j]可以转移到f[1][...],这些状态都是合法的,所以f[0][j]=1.f[i][0]同理.
这里链一个讲的非常好的博客
#include<bits/stdc++.h>
using namespace std;
const int N = 1000+5;
const int mod = 1e9+7;
int n, f[N][N*2], ans = 0, a[N], b[N], size = 0, tmp[N*2], cnt = 0, L[N], R[N], inv[N];
int main(){
ios::sync_with_stdio(false);
cin >> n, inv[1] = 1;
for(int i = 2; i <= n; i++) inv[i] = 1ll*(mod-mod/i)*inv[mod%i]%mod;
for(int i = 1; i <= n; i++)
cin >> a[i] >> b[i], tmp[++cnt] = a[i], tmp[++cnt] = b[i]+1;
sort(tmp+1, tmp+cnt+1), size = unique(tmp+1, tmp+cnt+1)-tmp-1;
for(int i = 1; i <= n; i++){
L[i] = lower_bound(tmp+1, tmp+size+1, a[i])-tmp;
R[i] = lower_bound(tmp+1, tmp+size+1, b[i]+1)-tmp;
}
for(int i = 0; i < size; i++) f[0][i] = 1;
for(int i = 1; i <= n; i++){
f[i][0] = 1; //
for(int j = L[i]; j < R[i]; j++){
int len = tmp[j+1]-tmp[j], c = len-1, m = 1; //
f[i][j] = 1ll*f[i-1][j-1]*len%mod;
for(int k = i-1; k >= 1; k--){
if(L[k] > j || j >= R[k]) continue;
m++, c = 1ll*c*(len+m-2)%mod*inv[m]%mod; //
(f[i][j] += 1ll*f[k-1][j-1]*c%mod) %= mod;
}
}
for(int j = 1; j < size; j++)
f[i][j] = ((long long)f[i][j]+f[i-1][j]+f[i][j-1]-f[i-1][j-1]+mod)%mod;
}
cout << (f[n][size-1]-1+mod)%mod << endl;
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步