zoukankan      html  css  js  c++  java
  • [组合][DP]luogu P3643 [APIO2016]划艇

    题面

    https://www.luogu.com.cn/problem/P3643

    对于一个序列,第i项可取的值在{0}∪[ai,bi]之间,求使序列非零部分单调递增的方案数

    分析

    设 $f[i][j]$ 表示第 i 位选择的值为 j 的方案数,则有

    $f[i][j]=sum_{k=0}^{i-1}sum_{l=1}^{j-1} f[k][l] (jin[a_i,b_i])$

    $f[i][j]=0 (j otin[a_i,b_i])$

    很容易发现这个方程问题在于 j 的状态数过多,存不下也不能跑

    那么考虑离散化取值,就会离散化成 2n 个区间

    状态 $f[i][j]$ 变为第 i 位选择的值在第 j 个区间里的方案数

    此时转移时 1~i-1 中的学校分为了两类,一类是选择的值没在第 j 个区间里的,一类是在里面的

    取值没在第 j 个区间里的不需要额外考虑,在第 j 个区间里的则显然存在可组合情况(因为强制有序所以不是排列情况)

    设当前枚举到第 k 个学校, k+1~i 里有 p 个学校可在第 j 个区间选择里,那么组合方案数则为 $inom{len_j+p}{p}$

    考虑简单证明,往 $len_j$ 个值中增加 p 个 0 ,组合出来的结果除去 0 后强制有序显然单调递增,即 $inom{len_j}{p}$ 为从 $len_j$ 选择 p 个值并单调递增的方案数

    加入的 p 个 0 对应着每个位置不选择值,所以是正确的

    考虑转移方程中的该式子,因为第i个学校必须选,所以只加入 p-1 个 0 ,即 $inom{len_j+p-1}{p}$

    $f[i][j]=sum_{k=0}^{i-1}sum_{l=1}^{j-1} inom{len_j+p-1}{p} imes f[k][l] (jin[a_i,b_i])$

    $f[i][j]=0 (j otin[a_i,b_i])$

    $sum_{l=1}^{j-1} f[k][l]$ 可以前缀和,注意预处理组合数减小常数即可

    代码

    #include <iostream>
    #include <cstdio>
    #include <algorithm>
    using namespace std;
    typedef long long ll;
    const ll P=1e9+7;
    const int N=510;
    int n,m;
    int a[N],b[N],c[2*N];
    ll f[N],inv[N],C[N],ans;
    
    ll Pow(ll x,ll y) {ll ans=1;for (;y;y>>=1,x=x*x%P) if (y&1) ans=ans*x%P;return ans;}
    
    int main() {
        scanf("%d",&n);
        for (int i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]),c[i*2-1]=a[i],c[i*2]=++b[i],inv[i]=Pow(i,P-2);
        sort(c+1,c+2*n+1);m=unique(c+1,c+2*n+1)-c-1;
        for (int i=1;i<=n;i++) a[i]=lower_bound(c+1,c+m+1,a[i])-c,b[i]=lower_bound(c+1,c+m+1,b[i])-c;
        C[0]=f[0]=1;
        for (int k=2,l;k<=m;k++) {
            l=c[k]-c[k-1];for (int i=1;i<=n;i++) C[i]=C[i-1]*(l-1+i)%P*inv[i]%P;
            for (int i=n;i;i--)
                if (a[i]<k&&k<=b[i]) {
                    ll h=0;
                    for (int j=i-1,cnt=1;j>=0;j--) {
                        (h+=C[cnt]*f[j]%P)%=P;
                        if (a[j]<k&&k<=b[j]) cnt++;
                    }
                    (f[i]+=h)%=P;
                }
        }
        for (int i=1;i<=n;i++) (ans+=f[i])%=P;
        printf("%lld
    ",ans);
    }
    View Code
    在日渐沉没的世界里,我发现了你。
  • 相关阅读:
    Java编程的逻辑 (62)
    float示例
    如何避免在短时间内按钮被多次重复点击
    前端(jQuery)(9)-- jQuery菜单
    前端(jQuery)(8)-- jQuery元素遍历
    前端(jQuery)(6)-- jQuery的扩展与noConflict
    前端(jQuery)(5)-- jQuery AJAX异步访问和加载片段
    xampp中tomcat服务器无法启动
    前端(jQuery)(4)-- jQuery隐藏显示与淡入淡出、滑动、回调
    自定义事件总结
  • 原文地址:https://www.cnblogs.com/mastervan/p/14576975.html
Copyright © 2011-2022 走看看