先来看一道题目
- 问题:给出每个人(n<=100000)的五门学科成绩,求出所有人:五门学科名次都比自己靠前的同学的人数
开始的确想过用集合,但是两秒放弃,因为无法用充分利用二进制,集合手动还得合并,而且得到集合的过程也得一一成绩对比,好像不可能完成。
oh,然后hihocoder介绍了强无敌的bitset,以前见过,但是没仔细看,因为那次只有60位,我好像用string解决的,没想到有这么强大(欠打)。
下面介绍以下bitset的基本使用信息,(大多是别人的东西,筛选后复制过来的),详细的还请移步百度文库:
和int long的比较 :
一般,int占4个字节,long占8个字节。而一个字节是由8个位组成的。
粗略估计,int和BitSet的比例为4*8:1,即32:1(因为bitset只保存0或者1,一位就够了)。如果是long,差距就更大了。(所以你能想象开到上亿的数组感觉多么快乐吗。。。。)
和vector的比较:
类似于vector,bitset类是一种类模板;而与vector不一样的是bitset类型对象的区别仅在其长度而不在其类型。在定义bitset时,要明确bitset含有多少位,须在尖括号内给出它的长度值:
bitset<32> bitvec; //32位,全为0。
给出的长度值必须是常量表达式比如16,32,1313520,4008208820,etc。正如这里给出的,长度值必须定义为整型字面值常量或是已用常量值初始化的整数类型的const对象。
这条语句把bitvec定义为含有32个位的bitset对象。和vector的元素一样,bitset中的位是没有命名的,程序员只能按位置来访问它们。位集合的位置编号从0开始,因此,bitvec的位序是从0到31。以0位开始的位串是低阶位(low-order bit),以31位结束的位串是高阶位(high-order bit)。
bitset的初始化:
bitset<maxn> b; |
b有maxn位,每位都为0 |
bitset<maxn> b(u); |
b是unsigned long型u的一个副本 |
bitset<maxn> b(s); |
b是string对象s中含有的位串的副本 |
bitset<maxn> b(s, pos, n); |
b是s中从位置pos开始的n个位的副本 |
(string和int的方向有点差别,string是从右往左)
本题bitset的巧妙:
22,23行充分利用了相邻排名的关系,如果不用二进制,一一传递很费时。
26,28行充分利用了二进制的交集运算,如果不用二进制,也很麻烦。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<iostream> 4 #include<cstring> 5 #include<algorithm> 6 #include<bitset> 7 using namespace std; 8 const int maxn=30010; 9 int Rank[maxn][6],sa[maxn][6],ans; 10 bitset<maxn+2>Set[maxn][6],tmp; 11 int main() 12 { 13 int n,i,j; 14 scanf("%d",&n); 15 for(i=1;i<=n;i++) 16 for(j=1;j<=5;j++){ 17 scanf("%d",&sa[i][j]); 18 Rank[sa[i][j]][j]=i; 19 } 20 for(j=1;j<=5;j++) 21 for(i=2;i<=n;i++){ 22 Set[i][j]=Set[i-1][j]; 23 Set[i][j].set(Rank[i-1][j]); 24 } 25 for(i=1;i<=n;i++){ 26 tmp=Set[sa[i][1]][1]; 27 for(j=2;j<=5;j++) 28 tmp&=Set[sa[i][j]][j]; 29 printf("%d\n",tmp.count()); 30 } 31 return 0; 32 }