牛客练习赛22 简单瞎搞题(bitset优化dp)

时间:2020-12-27 19:45:59
一共有 n个数,第 i 个数是 xi 
xi 可以取 [li , ri] 中任意的一个值。

设 牛客练习赛22 简单瞎搞题(bitset优化dp),求 S 种类数。

输入描述:

第一行一个数 n。 
然后 n 行,每行两个数表示 li,ri。
 

输出描述:

输出一行一个数表示答案。
示例1

输入

复制

5
1 2
2 3
3 4
4 5
5 6

输出

复制

26

备注:

1 ≤ n , li , ri ≤ 100

题意: 给出n个区间,你可以随机在每个区间挑一个数,然后他们的平方和,求每个区间挑数的所有情况平方和的种类数,当然就是平方和的数重复了只能算一个
思路:开始我看范围这么小,想写搜索来着,但是想了下还是会爆,我们可以dp处理,但是还是会超时,题解说要用bitset,去理解了下
首先说下bitset的应用

Bitset的基础用法及解释:

初始化bitset对象的方法

bitset<n> b;

b有n位,每位都为0

bitset<n> b(u);

b是unsigned long型u的一个副本

bitset<n> b(s);

b是string对象s中含有的位串的副本

bitset<n> b(s, pos, n);

b是s中从位置pos开始的n个位的副本

bitset操作

b.any()

b中是否存在值为1的二进制位?

b.none()

b中不存在值为1的二进制位吗?

b.count()

b中值为1的二进制位的个数

b.size()

b中二进制位的个数

b[pos]

访问b中在pos处的二进制位

b.test(pos)

b中在pos处的二进制位是否为1?

b.set()

把b中所有二进制位的值为1

b.set(pos)

把b中在pos处的二进制位值为1

b.reset()

把b中所有二进制位的值为0

b.reset(pos)

把b中在pos处的二进制位值为0

b.flip()

把b中所有二进制位逐位取反

b.flip(pos)

把b中在pos处的二进制位取反

b.to_ulong()

用b中同样的二进制位返回一个unsigned long值

os << b

把b中的位集输出到os流


反正我也不是很懂,
首先你要熟悉bitset是干嘛用的,不然你不懂他的解法>_<(说我自己>_<)
第一个好处:bitset相当于一个bool型的数组但是内存开销比bool小(bool是开了一个字节的空间嘛,然后bitset就是一个bit位)
第二个好处:bitset在合并的时候相当方便,因为bitset还可以进行位运算的操作,就是相当于一个数的二进制存储嘛,所以如果你要进行一个标记数组的合并的话,本来要进行很多次循环,但是bitset直接一个|就可以解决
还有就是你如果要改变一个数的标记的话,假如你本来标记了3你要改成6,你就只要左移三就行,因为这个bitset的<<,>>和普通数的相反,所以bitset相当于it一个很好用的标记数组,这也是我对bitset的一些个人理解,还有待改进
好,现在看题,我们结合bitset就可以很好解决这个题,我们直接一个一个区间遍历,标记他平方的所有数,然后第二个区间的标记就是在前面所有出现过的数上再加上这个区间每个数的平方,也就是<<操作,这里我用了一个滚动数组思想
开一个1000000的数组也可以
//bitset 第i个位置就代表数字i出现过没有
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + ;
bitset<maxn>ans, cnt;
int n;
int main()
{
scanf("%d", &n);
ans[] = ;
for (int i = ; i < n ; i++)
{
int x, y, k = ;
cnt.reset();
scanf("%d%d", &x, &y);
for (int j = x ; j <= y ; j++)
{
ans <<= (j * j - k);//把前一个区间的值加上这个区间的一个数的值再标记
//cout<<ans<<endl;
cnt |= ans;
k = j * j;
}
ans = cnt;
}
printf("%d\n", ans.count());
return ;
}