P3643/BZOJ4584 [APIO2016]划艇

题意:给出n个闭区间,从每个区间取一个整数,或者取0,要求非零数严格递增,问方案数。

发现N<=500,那么划分出的区间是一个比较小的数。

于是我们离散化区间,题目给出闭区间,转化成一个左闭右开区间。

定义f(i,j)为第i所学校最后参赛,且所选区间在第j个的方案数。

这时候前面的每个学校都分为两种,在区间内的和不在区间内的。

我们枚举最后一个不在区间内的学校p作为计数的基准点避免重复。

设m为[p+1,i]这个闭区间内在j区间内的学校个数,l为这个j区间的长度,可以发现选出学校就可以确定一种方案。

我们要确定这个方案数,即m个学校可以取j区间内还未取的数或0,其中当前的第i座学校必须取非0值的方案数,即为C(l+m-1,m) 

f(i,j)=∑k∑p f(p,k)*C(l+m-1,m)  [1<=k<=j-1;0<=p<=i-1],压掉j这一维,C在每一次枚举区间时进行预处理,并处理前缀和进行转移。

代码如下:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
const ll N=5e2+10,mod=1e9+7;
int n,m,ans,a[N],b[N],c[N*2],C[N],g[N],inv[N];
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
    return x*f;
}
int main(){
    n=read();inv[1]=1;
    for(int i=2;i<=n;i++) inv[i]=1ll*(mod-mod/i)*inv[mod%i]%mod;
    for(int i=1;i<=n;i++) c[++m]=a[i]=read(),b[i]=read(),c[++m]=b[i]+1;
    sort(c+1,c+1+m);m=unique(c+1,c+1+m)-(c+1);
    for(int i=1;i<=n;i++){
        a[i]=lower_bound(c+1,c+1+m,a[i])-c;
        b[i]=lower_bound(c+1,c+1+m,b[i]+1)-c;
    }
    C[0]=1;g[0]=1;
    for(int j=1;j<=m-1;j++){
        int len=c[j+1]-c[j];
        for(int i=1;i<=n;i++) C[i]=1ll*C[i-1]*(len+i-1)%mod*inv[i]%mod;
        for(int i=n;i>=1;i--) if(a[i]<=j&&j+1<=b[i]){
            int f=0,t=1,p=C[1];
            for(int k=i-1;~k;k--){
                f=1ll*(f+1ll*p*g[k]%mod)%mod;
                if(a[k]<=j&&j+1<=b[k]) p=C[++t];
            }
            g[i]=1ll*(g[i]+1ll*f)%mod;
        }
    }
    for(int i=1;i<=n;i++) ans=1ll*(ans+1ll*g[i])%mod;
    return printf("%d\n",ans),0;
}

 

posted @ 2019-05-09 13:00  小塘一点空明  阅读(163)  评论(0编辑  收藏  举报