【APIO2016】bzoj4584 赛艇

229 篇文章 0 订阅
175 篇文章 0 订阅

很容易想出一个dp算法,用 dp[i][j] 表示前 i 个学校,最后一个选颜色j的方案数。转移也很简单。但是这样时间和空间都无法承受。
于是我们可以离散化,但是这样光记录最后一个放在哪个区间是不够的了。可以用 dp[i][j][k] 表示前 i 个学校,第i个学校选了区间 j ,区间j总共放了 k 个的方案数。
转移也不复杂,dp[i][j][k]=dp[i1][j][k]+dp[i][j][k1]lenjk+1k,后一项的系数实际是 CklenjCk1lenj
注意当 k=1 时, dp[i][j][1]=dp[i1][j][1]+j1x=0i1y=0dp[i][x][y] ,其中后一项可以前缀和优化。
复杂度是 O(n3)
在uoj上可以通过此题。但是在bzoj上被卡常,优化方法是记录每个区间目前被多少个位置覆盖,从而减少对 k <script type="math/tex" id="MathJax-Element-2164">k</script>的枚举。

#include<cstdio>
#include<algorithm>
using namespace std;
#define LL long long
const int p=1000000007,maxn=510;
int dp[2*maxn][maxn],sum[2*maxn],lim[2*maxn],inv[maxn],ord[maxn*2],l[maxn],r[maxn],
n,m;
int rd()
{
    int x=0;
    char c=getchar();
    while (c<'0'||c>'9') c=getchar();
    while (c>='0'&&c<='9')
    {
        x=x*10+c-'0';
        c=getchar();
    }
    return x;
}
int pow(int b,int k)
{
    int ret=1;
    for (;k;k>>=1,b=(LL)b*b%p)
        if (k&1) ret=(LL)ret*b%p;
    return ret;
}
int inc(int x,int y)
{
    x+=y;return x>=p?x-p:x;
}
int dec(int x,int y)
{
    x-=y;return x<0?x+p:x;
}
int main()
{
    /*freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);*/
    int ans=0;
    n=rd();
    for (int i=1;i<=n;i++)
    {
        l[i]=rd();
        r[i]=rd()+1;
        ord[++m]=l[i];
        ord[++m]=r[i];
    }
    sort(ord+1,ord+m+1);
    m=unique(ord+1,ord+m+1)-ord-1;
    for (int i=1;i<=n;i++)
    {
        l[i]=lower_bound(ord+1,ord+m+1,l[i])-ord;
        r[i]=lower_bound(ord+1,ord+m+1,r[i])-ord;
    }
    for (int i=1;i<=n;i++) inv[i]=pow(i,p-2);
    dp[0][0]=1;
    for (int j=0;j<m;j++) sum[j]=1;
    for (int i=1;i<=n;i++)
    {
        for (int j=l[i];j<r[i];j++)
        {
            lim[j]++;
            for (int k=lim[j];k>1;k--)
                dp[j][k]=inc(dp[j][k],(LL)dp[j][k-1]*(ord[j+1]-ord[j]-k+1)%p*inv[k]%p);
            dp[j][1]=inc(dp[j][1],(LL)sum[j-1]*(ord[j+1]-ord[j])%p);
        }
        for (int j=1;j<m;j++)
        {
            sum[j]=sum[j-1];
            for (int k=1;k<=lim[j];k++)
                sum[j]=inc(sum[j],dp[j][k]);
        }
    }
    printf("%d\n",dec(sum[m-1],1));
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值