计数难题5:[APIO2016]划艇

Posted guessycb

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了计数难题5:[APIO2016]划艇相关的知识,希望对你有一定的参考价值。

计数难题5:[APIO2016]划艇

标签(空格分隔): 计数难题题选

题目大意:

给定(n)个区间([l_i,r_i])
你可以从第(i)个区间中选出一个整数元素(a_iin [l_i,r_i]),也可以不选。
要求选出的元素按标号顺序排列后构成一个严格单调递增序列。
求至少选出一个元素的合法方案数。
答案对(10^9+7)取模。
数据范围:(nleq 500) , (l_ileq 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)} inom{m-1}{l-1}inom{len}{l})]
显然(sum_{t=1}^{j-1} f_{k,t}) 是可以记前缀和的。
所以我们现在的问题变为求(sum_{l=1}^{min(m,len)} inom{m-1}{l-1}inom{len}{l})
我们有(sum_{l=1}^{min(m,len)} inom{m-1}{l-1}inom{len}{l} = sum_{l=1}^{min(m,len)} inom{m-1}{m-l}inom{len}{l})
所以可以用范德蒙恒等式化简:

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

唯一的问题就是如何求(inom{m+len-1}{m})了,因为(len)可能达到(10^9)级别。
注意到组合数的改变只与(m)有关。
所以组合数的变化是上下同时(+1)
所以可以套用吸收-归纳恒等式:(inom{n+1}{m+1} = frac{n+1}{m+1} inom{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 ; 
} 

以上是关于计数难题5:[APIO2016]划艇的主要内容,如果未能解决你的问题,请参考以下文章

P3643 [APIO2016]划艇 dp+组合数

[APIO2016]划艇

[BZOJ4584][Apio2016]赛艇

BZOJ4584[Apio2016]赛艇 DP

BZOJ 4584: [Apio2016]赛艇

#2567. 「APIO2016」划艇