首页 > 其他 > 详细

ドワンゴ6th予選E Span Covering

时间:2020-03-11 09:38:16      阅读:73      评论:0      收藏:0      [点我收藏+]

Link
在放的过程中,被覆盖的位置会形成若干个区间。而我们并不需要关心区间的位置,只需要关心其长度。
\(f_{i,j,k}\)表示用了\(i\)条线段,形成了\(j\)个区间,长度和为\(k\)的方案数。
我们将所有线段按\(L\)降序排序,然后依次枚举。
边界状态为\(f_{1,1,L_1}=1\)
对于\(f_{i-1,j,k}\),考虑加入长度为\(L_i\)的线段有什么可能的情况:
\(1.\)这条线段被某个区间完全覆盖(注意到我们是按长度降序枚举线段,因此之前的任何一个区间都比现在的线段要长):
\[(k-j(L_i-1))*f_{i-1,j,k}\rightarrow f_{i,j,k}\]
\(2.\)这条线段自成一个区间:
\[f_{i-1,j,k}\rightarrow f_{i,j+1,k+L_i}\]
\(3.\)这条线段与一个区间有重合部分,枚举未重合部分的长度\(l\)
\[2j*f_{i-1,j,k}\rightarrow f_{i,j,k+l}\]
\(4.\)这条线段与两个区间有重合部分,枚举未重合部分的长度\(l\)
\[(j-1)(L_i-l-1)*f_{i-1,j,k}\rightarrow f_{i,j-1,k+l}\]
最后的答案就是\(\sum\limits_{i=1}^nf_{n,i,m}\)
直接转移的时间复杂度是\(O(n^2m^2)\)的,注意到当\(i*j>m\)\(f_{i,j,k}=0\),因此可以优化至\(O(nm^2)\)

#include<cstdio>
#include<algorithm>
#include<functional>
const int P=1000000007;
int a[107],f[107][107][507];
int read(){int x;scanf("%d",&x);return x;}
void inc(int&a,int b){a+=b-P,a+=a>>31&P;}
int mul(int a,int b){return 1ll*a*b%P;}
int main()
{
    int n=read(),m=read(),ans=0;
    for(int i=1;i<=n;++i) a[i]=read();
    std::sort(a+1,a+n+1,[](int i,int j){return i>j;}),f[1][1][a[1]]=1;
    for(int i=2;i<=n;++i)
    for(int j=0;j<=i;++j)
        for(int k=0;k<=m;++k)
        if(f[i-1][j][k])
        {
            inc(f[i][j][k],mul(k-(a[i]-1)*j,f[i-1][j][k]));
            if(k+a[i]<=m) inc(f[i][j+1][k+a[i]],mul(j+1,f[i-1][j][k]));
            if(j) for(int l=1;l<a[i]&&k+l<=m;++l) inc(f[i][j][k+l],mul(j*2,f[i-1][j][k]));
            if(j>1) for(int l=0;l<a[i]&&k+l<=m;++l) inc(f[i][j-1][k+l],mul((j-1)*(a[i]-l-1),f[i-1][j][k]));
        }
    for(int i=1;i<=n;++i) inc(ans,f[n][i][m]);
    printf("%d",ans);
}

ドワンゴ6th予選E Span Covering

原文:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12460397.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!