洛谷p3643[APIO2016]划艇

【洛谷p3643】[APIO2016]划艇

题面

洛谷

题解

组合\(dp\)
我们设\(f[i][j]\)表示第\(i\)所学校参赛,并且派出了\(j\)艘划艇的方案数。
不难得到:
\[f_{0,1} = 1\]
\[f_{i,j} = j∈I_i ? \sum_{k=1}^{j-1}\sum_{p=0}^{i-1}f_{p,k}:0\]
答案即为\(f_{i,j}\)之和。
这时我们发现\(1\)个问题,那就是第二维的复杂度是\(10^9\)的,并不能承受。
于是我们考虑离散化。
离散化之后设\(f_{i,j}\)表示第\(i\)所学校参赛并且派出的划艇数在\(j\)这个区间里的方案数。
此时需要知道一点,
那就是从区间\([0,l]\)中取\(n\)个数,使得所有非\(0\)数严格递增,方案数为\(\binom{l+n}{n}\)
我们枚举前\(p\)所学校不在区间\(j\)中,则\(M\)\(p+1 \sim i\)号学校中能选区间\(j\)学校的数量。

\[f_{i,j}=\sum_{k=1}^{j-1} \sum_{p=0}^{i-1}\binom{l+M-1}{M}f_{p,k}\]
此时可以前缀和优化,这题就做完了。

代码

#include <bits/stdc++.h>

const int maxn = 510;
const int mod = 1e9 + 7;
typedef long long ll;
using std::sort;

int n, m, i, j, k, N;                            
int l[maxn], r[maxn], a[maxn << 1];             
int inv[maxn], c[maxn], f[maxn];

inline void get_inv(int n) {
    inv[1] = 1;    
    for(int i = 2;i <= n;i++)
        inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;     
    return;
}
inline int add(ll a,ll b) {
    ll c = a + b;
    return c >= mod ? c - mod : c;    
}

int main() {
    scanf("%d",&N);  
    get_inv(N);   
    for(int i = 1;i <= N;i++)
        scanf("%d %d",l + i,r + i), a[++n] = l[i], a[++n] = r[i] + 1;
    sort(a + 1,a + n + 1);
    n = std::unique(a + 1,a + n + 1) - a - 1;
    for(int i = 1;i <= N;i++)
        l[i] = std::lower_bound(a + 1,a + n + 1,l[i]) - a,
        r[i] = std::lower_bound(a + 1,a + n + 1,r[i] + 1) - a;
    c[0] = f[0] = 1;
    for(int j = 1;j < n;j++) {
        int le = a[j + 1] - a[j];
        for(int i = 1;i <= N;i++)
            c[i] = 1ll * c[i - 1] * (le + i - 1) % mod * inv[i] % mod;
        for(int i = N;i >= 1;i--) {
            if(l[i] <= j && j + 1 <= r[i]) {
                int o = 0, p = 1, t = le;
                for(int k = i - 1;~k;k--) {
                    o = add(1ll * o,1ll * t * f[k] % mod);
                    if(l[k] <= j && j + 1 <= r[k])
                        t = c[++p];   
                }
                f[i] = add(f[i],o);  
            }
        }
    }
    int ans = 0;
    for(int i = 1;i <= N;i++)
        ans = add(ans,f[i]);
    printf("%d\n",ans);
    return 0;      
}

转载于:https://www.cnblogs.com/Sai0511/p/11310350.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值