NC17193 简单瞎搞题

状态表示:\(f[i][j]\)表示前\(i\)个数是否能组成数\(j\)
状态转移:枚举\(x_i\)\([l_i,r_i]\)
时间复杂度最坏为:\(10^{10}\),显然不能接受
空间复杂度为:\(100 M\),可采用滚动数组优化

下面针对时间复杂度优化:

\(f[i][j]\) 为前\(i\)个数字能否构成j,考虑第\(i\)个数选还是不选。

显然如果\(f[i-1][j-x[i]^2]==1\)的话\(f[i][j]\)就是可以的,\(x[i]\)的取值是\(l[i]\)\(r[i]\)

这样的话其实是有点浪费的——f数组是一个只有01两个值的数组,哪怕表示成bool类型都有点多余。于是我们可以考虑用bitset来优化它。

bitset你可以理解为一个长度很长的01数字串(实际上它是用int拼接而成),也可也理解为可以用位运算的bool数组。

这样的话一行就可以一起求——如果我们用\(f[i]\)来表示第i行的01串那么:\(f[i]=f[i] | (f[i-1]<<(x[j]^2))\)

然后bitset自带一个求1的个数的函数count,这就非常美滋滋了。

特别说明:bitset内部实现是用int拼接而成所以时间复杂度是\(O(长度/32)\)的,那么本题的时间复杂度是\(O(n^5/32)\)(bitset最大长度也就是背包体积是\(n^3\),还要枚举n,枚举从\(l_i\)\(r_i\))

const int N=110,M=1e6+10;
int l[N],r[N];
bitset<M> f[N];
int n;
 
int main()
{
    cin>>n;
 
    for(int i=1;i<=n;i++) cin>>l[i]>>r[i];
 
    f[0][0]=1;
    for(int i=1;i<=n;i++)
        for(int j=l[i];j<=r[i];j++)
            f[i] |= f[i-1]<<(j*j);
 
    cout<<f[n].count()<<endl;
 
    //system("pause");
}

滚动数组优化:注意每轮清空

const int N=110,M=1e6+10;
int l[N],r[N];
bitset<M> f[2];
int n;
 
int main()
{
    cin>>n;
 
    for(int i=1;i<=n;i++) cin>>l[i]>>r[i];
 
    f[0][0]=1;
    for(int i=1;i<=n;i++)
    {
        f[i&1].reset();
        for(int j=l[i];j<=r[i];j++)
            f[i&1] |= f[i-1&1]<<(j*j);
    }
 
 
    cout<<f[n&1].count()<<endl;
 
    //system("pause");
}
posted @ 2020-10-01 18:22  Dazzling!  阅读(119)  评论(0编辑  收藏  举报