hihocoder 1513 小Hi的烦恼 五维数点求集合的交集 bitset用法

来源:互联网 发布:淘宝延长收货如何设置 编辑:程序博客网 时间:2024/06/05 15:50

题目链接

官方题解:

小Hi:今天我们来解决“五维数点”问题。

小Ho:什么是“五维数点”问题呢?

小Hi:抽象来说,我们现在有n个在五维空间中的点(X_i,Y_i,Z_i,Q_i,W_i)。现在对于每个点,我们需要知道所有坐标均比它小的点的数量。

小Ho:这个问题看起来似曾相似,如果是在二维空间中似乎是一个经典的运用线段树解决的题目。

小Hi:对。但是现在是在五维空间中,看起来难度大了很多。

小Ho:我想用集合的角度去考虑这个问题。对于每一维,比如说X维,我们能通过按X维的坐标排序,不难求出对i点来说X维比i点小的所有点的集合。现在的问题就转化成对点i来说,求出X维,Y维,Z维,Q维,W维分别比i所在那一维小的集合的交的大小。

小Hi:对!你的思路很好。但是集合大小是O(N^2),如果暴力实现的话时间复杂度就达到了O(N^2)。

小Ho:那该怎么办呢?

小Hi:我们想想可以用什么合理的方法来表示集合以此来加快求集合交的操作。

小Ho:我觉得一种比较直观的方法是用一个长度为n的01串,第i位为0表示i不在集合中,1表示i在集合中。

小Hi:不错哟!那你仔细观察一下,求集合的交到底具有什么性质?比如对于n=6,集合{1,4,5}和集合{2,4,5,6}来说,它们的交是{4,5}。

小Ho:集合求交在01串中可以这么看:若两串第i位都是1,则交的串第i位是1,否则第i位就是0。这个例子中两个集合的01串分别为100110,010111。它们的交就是000110,也就是{4,5}。

小Hi:是的。我们把这个问题转化成了对01串的操作。你有没有发现,这其实类似于二进制中"and"的操作。如果我们把01串看成一个二进制的大整数,那么集合求交就变成了对两个大整数做"and"的操作。

小Ho:哈!有道理。但是这看起来复杂度似乎依旧是O(N^2)的。

小Hi:啊!但是你有没有想过,我们可以利用程序语言中的32位整数加速这个"and",也就是说我们每32位压缩成一个32位整数,这样本来我们需要32次的操作,一下就变成了做一次位运算“并”的操作。所有我们最后的复杂度能优化成O(N^2/32)。

小Ho:原来如此!那具体怎么实现这个“压缩”的过程呢?

小Hi:其实c++/Java已经为我们设计了这样一种数据结构来解决这种问题。它的名字叫bitset/BitSet。它类似于数组,但是你可以直接对其做位运算。bitset中还有一些有用的函数,如count/cardinality可以快速算出二进制中有多少个1(这其实是一个不太好做的问题)。 如上述例子中的代码实现:

bitset <6> a,b,c;a[1] = a[4] = a[5] = 1;b[2] = b[4] = b[5] = b[6] = 1;c = a & b;cout << c << endl;cout << "size = " << c.count() << endl;

小Ho:了解了!我这就去实现一下。


思路:

通过这道题呢我也是头一次听说了bitset这个数据结构,通过它我们可以快速求

两个集合的交集,当然前提是我们把两个集合对应的元素转化为二进制串,对应位置的

元素有就是1否则就是0,bitset就直接完成了两个二进制串的与操作并可以直接返回

二进制中1的个数,(当然我们求两个集合的交集也可以用STL库对集合A的每个

元素去在B中find)

首先简单介绍一下bitset用法;

头文件为<bitset>

在定义bitset时,要明确bitset含有多少位,须在尖括号内给出它的长度值:如 bitset<32>bt 初始化全为0

一、bitset的初始化

1. unsigned值初始化bitset对象

当用unsigned long值作为bitset对象的初始值时,该值将转化为二进制的位模式。而bitset对象中的位集作为这种位模式的副本。如果bitset类型长度大于unsigned long的二进制位数,则其余的高阶位置为0;如果bitet类型长度小于unsigned long的二进制位数,则只使用unsigned值中的低阶位,超过bitet类型长度的高阶位将被丢弃。

32unsigned long的机器上,十六进制值0xffff表示为二进制位就是十六个1和十六个0(每个0xf可表示为1111)。可以用0xffff初始化bitset对象:

// bitvec1 is smaller than the initializer

bitset<16> bitvec1(0xffff);          // bits 0 ... 15 are set to 1

// bitvec2 same size as initializer

bitset<32> bitvec2(0xffff);          // bits 0 ... 15 are set to 1; 16 ... 31 are 0

// on a 32-bit machine, bits 0 to 31 initialized from 0xffff

bitset<128> bitvec3(0xffff);         // bits 32 through 127 initialized to zero

上面的三个例子中,015位都置为1。由于bitvec1位数少于unsigned long的位数,因此bitvec1的初始值的高阶位被丢弃。bitvec2unsigned long长度相同,因此所有位正好放置了初始值。bitvec3长度大于3231位以上的高阶位就被置为0

2. string对象初始化bitset对象

当用string对象初始化bitset对象时,string对象直接表示为位模式。从string对象读入位集的顺序是从右向左

string strval("1100");

bitset<32> bitvec4(strval);

bitvec4的位模式中23的位置为1,其余位置都为0。如果string对象的字符个数小于bitset类型的长度,则高阶位将置为0

string象和bitset对象之间是反向转化的:string对象的最右边字符(即下标最大的那个字符)用来初始化bitset对象的低阶位(即下标为0的位)。当用string对象初始化bitset对象时,记住这一差别很重要。

不一定要把整个string对象都作为bitset对象的初始值。相反,可以只用某个子串作为初始值:

string str("1111111000000011001101");

bitset<32> bitvec5(str, 5, 4); // 4 bits starting at str[5], 1100

bitset<32> bitvec6(str, str.size() - 4);     // use last 4 characters

这里用str中从str[5]开始包含四个字符的子串来初始化bitvec5

二、bitset的一些基本操作

bitset<32>b;

 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

参考:http://blog.163.com/lixiangqiu_9202/blog/static/53575037201251121331412/

这道题的话如果按照官方给出的思路做的话还是会超时,虽然优化了一点点但还是不够.我们要将所有的成绩转换为二进制串并要求

全部成绩都在其前面的人的个数.那么给定一个人的成绩,我们就要把其余的人中成绩在他前面的对应位置置为1,所以我们要先预处理

对应成绩的名次是第几个小朋友,然后枚举名次将对应小朋友的位置置1,在对应每个小朋友将其五门课与最后求二进制串中的1的个数

即为所求.这样就可以在O(n)的时间内处理出来.

#include<bits/stdc++.h>#define Ri(a) scanf("%d", &a)#define Rl(a) scanf("%lld", &a)#define Rf(a) scanf("%lf", &a)#define Rs(a) scanf("%s", a)#define Pi(a) printf("%d\n", (a))#define Pf(a) printf("%lf\n", (a))#define Pl(a) printf("%lld\n", (a))#define Ps(a) printf("%s\n", (a))#define W(a) while(a--)#define CLR(a, b) memset(a, (b), sizeof(a))#define MOD 100000007#define inf 0x3f3f3f3f#define exp 0.00000001#define  pii  pair<int, int>#define  mp   make_pair#define  pb   push_backusing namespace std;typedef long long ll;const int maxn=1e4+10;int a[3*maxn][5],id[3*maxn][5];bitset<3*maxn>b[5][3*maxn],ans;int main(){int n;Ri(n);CLR(id,0);for(int i=1;i<=n;i++){for(int j=0;j<5;j++){   Ri(a[i][j]);   id[a[i][j]][j]=i;//每一门课的第几名分别是哪一个小朋友}}for(int i=2;i<=n;i++){for(int j=0;j<5;j++){b[j][i]=b[j][i-1];//枚举名次进行操作b[j][i].set(id[i-1][j]);//对于成绩为i的同学只需将其前面那些变为1}}for(int i=1;i<=n;i++){ans.set();for(int j=0;j<5;j++)ans&=b[j][a[i][j]];Pi(ans.count());//对于每个小朋友求其集合的交集.}return 0;}

1 0
原创粉丝点击