peiwenjun's blog 没有知识的荒原

P3643 [APIO2016] 划艇 题解

题目描述

给定 \(n\) 个区间 \([a_i,b_i]\) ,第 \(i\) 个数要么不选,要么在 \([a_i,b_i]\) 中选择。

要求选择的数严格递增,求方案数对 \(10^9+7\) 取模的结果。

数据范围

  • \(1\le n\le 500,1\le a_i\le b_i\le 10^9\)

时间限制 \(\texttt{3s}\) ,空间限制 \(\texttt{125MB}\)

分析

容易想到一个简单的 \(\texttt{dp}\)\(f_{i,j}\) 表示考虑前 \(i\) 个数,最后一个被选的数为 \(j\) 的方案数。

然而值域过大,所以这条路行不通。

将端点离散化,在一个长为 \(len\) 的区间中选 \(j\) 个严格递增的数的方案数为 \(\binom{len}j\)

重新设计状态, \(f_{i,j,k}\) 表示考虑前 \(i\) 个数,最后一个被选的数在第 \(j\) 个区间中,这个区间已经选了 \(k\) 个数的方案数。

转移需要分类讨论:

  • 不选: \(f_{i,j,k}\gets f_{i-1,j,k}\)
  • 新开一个区间: \(\forall l\le x\lt r,f_{i,x,1}\gets\sum_{j=0}^{x-1}\sum_{k=0}^if_{i-1,j,k}\cdot\binom{len_j}k\)
  • 不新开区间: \(\forall l\le x\lt r,f_{i,x,k}\gets f_{i-1,x,k-1}\)

递推预处理 \(\binom{len_j}k\) 的值,再用前缀和优化即可做到 \(\mathcal O(n^3)\)

#include<bits/stdc++.h>
using namespace std;
const int maxn=1005,mod=1e9+7;
int m,n,res;
int a[maxn],b[maxn],h[maxn],inv[maxn];
int c[maxn][maxn];
int f[2][maxn][505];
inline int qpow(int a,int k)
{
    int res=1;
    for(;k;a=1ll*a*a%mod,k>>=1) if(k&1) res=1ll*res*a%mod;
    return res;
}
inline void add(int &x,int y)
{
    if((x+=y)>=mod) x-=mod;
}
int main()
{
    scanf("%d",&n),inv[0]=c[0][0]=1;
    for(int i=1;i<=n;i++)
    {
        scanf("%d%d",&a[i],&b[i]);
        h[++m]=a[i],h[++m]=b[i]+1,inv[i]=1ll*inv[i-1]*qpow(i,mod-2)%mod;
    }
    sort(h+1,h+m+1);
    m=unique(h+1,h+m+1)-h-1;
    for(int i=1;i<m;i++)
    {
        int len=h[i+1]-h[i];
        for(int j=0,cur=1;j<=n;cur=cur*(len+1ll-++j)%mod) c[i][j]=1ll*cur*inv[j]%mod;
    }
    f[0][0][0]=1;
    for(int i=1;i<=n;i++)
    {
        memcpy(f[i&1],f[(i-1)&1],sizeof(f[i&1]));
        int l=lower_bound(h+1,h+m+1,a[i])-h;
        int r=lower_bound(h+1,h+m+1,b[i]+1)-h;
        for(int j=0,cur=0;j<r;j++)
        {
            if(j>=l)
            {
                add(f[i&1][j][1],cur);
                for(int k=1;k<=i;k++) add(f[i&1][j][k],f[(i-1)&1][j][k-1]);
            }
            for(int k=0;k<=i;k++) cur=(cur+1ll*f[(i-1)&1][j][k]*c[j][k])%mod;
        }
    }
    for(int j=1;j<m;j++) for(int k=1;k<=n;k++) res=(res+1ll*f[n&1][j][k]*c[j][k])%mod;
    printf("%d\n",res);
    return 0;
}

posted on 2023-05-19 16:51  peiwenjun  阅读(8)  评论(0)    收藏  举报

导航