[BZOJ4584][Apio2016]赛艇
[BZOJ4584][Apio2016]赛艇
试题描述
在首尔城中,汉江横贯东西。在汉江的北岸,从西向东星星点点地分布着个划艇学校,编号依次为到。每个学校都拥有若干艘划艇。同一所学校的所有划艇颜色相同,不同的学校的划艇颜色互不相同。颜色相同的划艇被认为是一样的。每个学校可以选择派出一些划艇参加节日的庆典,也可以选择不派出任何划艇参加。如果编号为的学校选择派出划艇参加庆典,那么,派出的划艇数量可以在Ai至Bi之间任意选择(Ai<=Bi)。值得注意的是,编号为i的学校如果选择派出划艇参加庆典,那么它派出的划艇数量必须大于任意一所编号小于它的学校派出的划艇数量。输入所有学校的Ai、Bi的值,求出参加庆典的划艇有多少种可能的情况,必须有至少一艘划艇参加庆典。两种情况不同当且仅当有参加庆典的某种颜色的划艇数量不同
输入
第一行包括一个整数N,表示学校的数量。接下来N行,每行包括两个正整数,用来描述一所学校。其中第行包括的两个正整数分别表示Ai,Bi(1<=Ai<=Bi<=10^9),N<=500
输出
输出一行,一个整数,表示所有可能的派出划艇的方案数除以1,000,000,007得到的余数
输入示例
2 1 2 2 3
输出示例
7
数据规模及约定
见“输入”
题解
首先离散成不超过 2n 个区间;设 f(i, j, k) 表示考虑前 i 个学校,其中最后 k 个学校在区间 j 中的方案数。
转移就是:
f(i, j, k) = f(i - 1, j, k) + f(i - 1, j, k - 1) / C(len[j], k-1) * C(len[j], k) (k > 1)
f(i, j, 1) = f(i - 1, j, k) + ∑1≤x<j∑1≤y≤min(len[x], i-1) f(i - 1, x, y)
其中,len[j] 表示第 j 段区间的长度,C(a, b) 表示组合数(从 a 元素中选 b 个的方案数)。
对于第二个式子搞一下前缀和优化就好了。
#include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <cctype> #include <algorithm> using namespace std; const int BufferSize = 1 << 16; char buffer[BufferSize], *Head, *Tail; inline char Getchar() { if(Head == Tail) { int l = fread(buffer, 1, BufferSize, stdin); Tail = (Head = buffer) + l; } return *Head++; } int read() { int x = 0, f = 1; char c = Getchar(); while(!isdigit(c)){ if(c == '-') f = -1; c = Getchar(); } while(isdigit(c)){ x = x * 10 + c - '0'; c = Getchar(); } return x * f; } #define maxn 510 #define MOD 1000000007 #define LL long long int n, num[maxn<<1], cntn; struct Line { int l, r; Line() {} Line(int _, int __): l(_), r(__) {} int len() { return r - l + 1; } } sch[maxn], ls[maxn<<1]; int invnum[maxn], C[maxn<<1][maxn], invC[maxn<<1][maxn]; void gcd(LL a, LL b, LL& x, LL& y) { if(!b){ x = 1; y = 0; return ; } gcd(b, a % b, y, x); y -= a / b * x; return ; } int Inv(LL a) { LL x, y; gcd(a, MOD, x, y); return (x % MOD + MOD) % MOD; } void init() { for(int i = 1; i <= n; i++) invnum[i] = Inv(i); for(int i = 1; i < cntn; i++) { C[i][0] = invC[i][0] = 1; for(int j = 1; j <= min(ls[i].len(), n); j++) C[i][j] = (LL)C[i][j-1] * (ls[i].len() - j + 1) % MOD * invnum[j] % MOD, invC[i][j] = Inv(C[i][j]); } return ; } int f[maxn<<1][maxn], sum[maxn<<1]; int main() { n = read(); for(int i = 1; i <= n; i++) { int l = read(), r = read() + 1; sch[i] = Line(l, r); num[++cntn] = l; num[++cntn] = r; } sort(num + 1, num + cntn + 1); cntn = unique(num + 1, num + cntn + 1) - num - 1; for(int i = 1; i < cntn; i++) ls[i] = Line(num[i], num[i+1] - 1); for(int i = 1; i <= n; i++) sch[i].l = lower_bound(num + 1, num + cntn + 1, sch[i].l) - num, sch[i].r = lower_bound(num + 1, num + cntn + 1, sch[i].r) - num; init(); for(int j = 0; j < cntn; j++) sum[j] = 1; for(int i = 1; i <= n; i++) { for(int j = sch[i].r - 1; j >= sch[i].l; j--) { for(int k = min(i, ls[j].len()); k >= 2; k--) { f[j][k] += (LL)f[j][k-1] * invC[j][k-1] % MOD * C[j][k] % MOD; if(f[j][k] >= MOD) f[j][k] -= MOD; } f[j][1] += (LL)sum[j-1] * ls[j].len() % MOD; if(f[j][1] >= MOD) f[j][1] -= MOD; } sum[0] = 1; for(int j = 1; j < cntn; j++) { sum[j] = sum[j-1]; int mxk = min(i, ls[j].len()); for(int k = 1; k <= mxk; k++) { sum[j] += f[j][k]; if(sum[j] >= MOD) sum[j] -= MOD; } } } printf("%d\n", ((sum[cntn-1] - sum[0]) % MOD + MOD) % MOD); return 0; }
终于卡过去了。。。