hihoCoder 1513 小Hi的烦恼 bitset位运算好题

http://www.elijahqi.win/2018/01/15/hihocoder-1513-%e5%b0%8fhi%e7%9a%84%e7%83%a6%e6%81%bc/
1513 : 小Hi的烦恼
Time Limit:5000ms
Case Time Limit:1000ms
Memory Limit:1024MB
描述

小Hi从小的一大兴趣爱好就是学习,但是他发现尽管他认真学习,依旧有学神考的比他好。

小Hi在高中期间参加了市里的期末考试,一共五门:语文、数学、英语、物理、化学。

成绩出来之后,小Hi发现有些同学,所有科目都考的比他好,他很烦恼。所以他想知道所有科目都比自己名次靠前的同学的人数。

为了方便,可以认为不存在两个人某一门名次是相同的。

其他同学们也想知道有多少人全面碾压了他们,所以你需要对所有人输出答案。

解题方法提示
输入

第一行,一个正整数N(N <= 30000),表示人数。

接下来N行,每行五个整数,分别表示五门课依次的排名。
输出

输出共N行,每行一个整数,表示答案。

Sample Input

4
1 1 2 2 1
2 3 3 3 2
3 2 1 1 3
4 4 4 4 4

Sample Output

0
1
0
3

stl暴力..

利用bitset的暴力 复杂度N^2/32

怎么搞 设sa[i][j]表示第i个人第j门学课的排名是多少 rk[i][j]表示第j个学课排名第i的人是谁

每次把这些人放入bitset 然后询问一个人的个数的时候相当于是集合去并用bitset加速即可

小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:了解了!我这就去实现一下。
这里写图片描述

#include<cstdio>
#include<bitset>
#define N 33000
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0;char ch=gc();
    while(ch<'0'||ch>'9') ch=gc();
    while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=gc();
    return x;
}
bitset<N>set[N][6],tmp;
int n,sa[N][6],rk[N][6];
int main(){
    freopen("1513.in","r",stdin);
    n=read();
    for (int i=1;i<=n;++i) for (int j=1;j<=5;++j) sa[i][j]=read(),rk[sa[i][j]][j]=i;
    for (int i=2;i<=n;++i)
        for (int j=1;j<=5;++j) 
            set[i][j]=set[i-1][j],set[i][j].set(rk[i-1][j]);
    for (int i=1;i<=n;++i){
        tmp=set[sa[i][1]][1];
        for (int j=2;j<=5;++j) tmp&=set[sa[i][j]][j];printf("%d\n",tmp.count());
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值