【APIO2016】bzoj4584 赛艇

很容易想出一个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 的枚举。

#include
#include
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];jfor (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));
}

你可能感兴趣的:(动态规划,数学,bzoj)