一共有 n个数,第 i 个数是
x
i
x_i
xi ,
x
i
x_i
xi 可以取
[
l
i
,
r
i
]
[l_i , r_i]
[li,ri] 中任意的一个值。
设
S
=
∑
x
i
2
S = \sum{{x_i}^2}
S=∑xi2, 求 S 种类数。
这一题主要用的是分类背包的思想,
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]表示的是考虑到第
i
i
i个区间,S是否能取到
j
j
j,取到为1,取不到为0。
进行三重循环遍历(第一层区间数
n
n
n,第二层
j
j
j的取值可以到达
n
3
n^3
n3,第三层为区间中的每个数遍历为
n
n
n),总复杂度到达了
O
(
n
5
)
O(n^5)
O(n5)。
这样进行动态规划的话比较浪费时间和内存,因为每个状态只有01两种取值,因此可以用 bitset 进行优化, 将
1
i
n
t
1 \ int
1 int压成
1
b
i
t
1 \ bit
1 bit。
bitset的基本用法: (bitset可以进行各种位操作,以及
c
o
u
n
t
,
s
e
t
count,set
count,set方法的使用)
可以
d
p
[
i
]
dp[i]
dp[i]表示第
i
i
i个区间可以表示的S(S的最大取值为
1
0
6
10^6
106,因此bitset最多需要
1
0
6
10^6
106位), 对于区间中的每个数
j
j
j,可以得出这样的转移方程
d
p
[
i
]
∣
=
d
p
[
i
−
1
]
<
<
(
j
∗
j
)
dp[i] \ |= \ dp[i-1]<<(j*j)
dp[i] ∣= dp[i−1]<<(j∗j), 这样复杂度降到了
O
(
n
/
32
)
O(n/32)
O(n/32), 并且其中的位操作是更快的。
最后答案即为
d
p
[
n
]
dp[n]
dp[n]中1的个数,可以用
c
o
u
n
t
(
)
count()
count()方法得到。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e6 + 10;
const int INF = 0x3f3f3f3f;
bitset<maxn> dp[102];
int n, l ,r;
int main()
{
scanf("%d", &n);
dp[0].set(0); //第0位要置1
for(int i = 1; i <= n; i++)
{
scanf("%d %d", &l, &r);
for(int j = l; j <= r; j++)
dp[i] |= dp[i-1]<<(j * j);
}
printf("%d\n", dp[n].count());
}
因篇幅问题不能全部显示,请点此查看更多更全内容