BZOJ 4584 [Apio2016]赛艇

本文介绍了一种利用区间离散化结合动态规划(DP)解决特定问题的方法。通过对区间进行离散化处理,将原问题转化为更易于解决的形式,并通过动态规划求解最优解。文章详细阐述了离散化过程、状态定义、状态转移方程等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

区间离散化+DP

AiBi太大了,一个数一个数地做肯定不行。于是先按照区间端点把区间离散化,至多形成2n1个区间。

f[i][j][k]表示做到第i个学校,最后一个数在离散区间j中,且第j个区间内有k个学校的方案数。

f[i][j][1]可以由f[i-1][j][1]和所有f[in][jm][k]转移过来,于是DP的时候记下前缀和来统计。

f[i][j][k](k1)+=f[i1][j][k1](k+1)/k

离散化的时候注意前一个区间的右端点和后一个区间的左端点不要重复,否则会有一颗赛艇的事情发生,我的方法是右端点减一。
第一维并没有什么用,把它压掉就好了- -

这题真excting,各种优化常数之后,跑了64秒过- -

#include<cstdio>
#include<algorithm>
#define N 505
#define MOD 1000000007 
#define reg register
using namespace std;
int in()
{
    reg char c=getchar();
    reg int r=0;
    while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9')r=r*10+c-'0',c=getchar();
    return r;
}
struct Sec{int l, r, len;}s[N<<1];int scnt=0, m=0;
int n, A[N], B[N], f[2*N][N], sum[2*N], inv[600], p[N<<1];
inline int query_l(int x)  
{  
    reg int l=1,r=scnt,ans;  
    for (;l<=r;)  
    {  
        int mid=(l+r)>>1;  
        if (x<=s[mid].l) ans=mid,r=mid-1; else l=mid+1;  
    }  
    return ans;  
}  

inline int query_r(int x)  
{  
    reg int l=1,r=scnt,ans;  
    for (;l<=r;)  
    {  
        int mid=(l+r)>>1;  
        if (s[mid].r<x) ans=mid,l=mid+1; else r=mid-1;  
    }  
    return ans;  
} 
void init()
{
    sort(p+1,p+2*n+1);
    for(reg int i = 1, ii=2*n; i < ii; i++)
        if(p[i]!=p[i+1])
            s[++scnt]=(Sec){p[i],p[i+1]-1, p[i+1]-p[i]};
    for(reg int i = 1; i <= n; i++)
    {
        A[i]=query_l(A[i]);
        B[i]=query_r(B[i]);
    }
    inv[1]=1;
    for(reg int i = 2; i < N; i++)
        inv[i]=1ll*(MOD-MOD/i)*inv[MOD%i]%MOD;
}
void dp()
{
    for(reg int i = 0; i <= scnt; i++)
        sum[i]=1;
    for(reg int i = 1; i <= n; i++)
    {
        for(reg int j = A[i]; j <= B[i]; j++)
        {
            for(reg int k = min(i,s[j].len); k >= 2; k--)
                f[j][k]=(f[j][k]+((long long)f[j][k-1]*(s[j].len-k+1)%MOD)*inv[k])%MOD;
            f[j][1]=(f[j][1]+(long long)sum[j-1]*s[j].len%MOD)%MOD;
        }
        sum[0]=1;
        for(reg int j = 1; j <= scnt; j++)
        {
            sum[j]=sum[j-1];
            for(reg int k = 1; k <= min(i,s[j].len); k++)    
                sum[j]=(sum[j]+f[j][k])%MOD;
        }
    }
}
int main()
{
    n = in();
    for(int i = 1; i <= n; i++)
    {
        p[i*2-1]=A[i]=in();
        p[i*2]=B[i]=in()+1;
    }
    init(); dp();
    int ans=0;
    for(reg int i = 1; i <= scnt; i++)
        for(reg int j = 1; j <= n; j++)
            ans=(ans+f[i][j])%MOD;
    printf("%d\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值