P4932 浏览器

有\(n\)个数,\(x_1,x_2,\cdots,x_n\),问你有多少对\((u,v)\),使得\(x_u\operatorname{xor}x_v\)的二进制表示中有奇数个\(1\)

输入六个整数,\(n,a,b,c,d,x_0\)。

每个点的权值需要用如下的方式生成。

\(x_i = (ax_{i-1}^2 + bx_{i-1} + c) \bmod d\)

\(n\le 10^7,\max(x_i)\le 10^9,a,b,c,d,x_0\)在\(int\)范围


位运算的题,可以先考虑运算前后的数的某些量有何关系

比如这题,可以发现,只有\(x_u,x_v\)的二进制中\(1\)的个数一奇一偶,\(x_u\operatorname{xor}x_v\)的二进制用才有奇数个\(1\)

可以设\(x_u,x_v\)的二进制\(1\)有\(k\)位重合,也就是说这\(k\)位上两个数都是\(1\)

对于异或运算,只有某一位两数不同才是\(1\),它们有\(\text{偶数}+\text{奇数}-2k\)个数位不同,其实就是分别\(1\)的位数减去共同都是\(1\)的那\(k\)位

那么即为\(\text{偶数}+\text{奇数}-\text{偶数}=\text{奇数}\)


那么现在考虑如何快速地求一个数二进制中\(1\)的数量

首先有一种很显然的方法

inline int cnt1(int x){
reg int ret=0;
while(x) ret+=x&1,x>>=1;
return ret;
}

就是一位一位的数,复杂度\(O(\log x)\)

如果此题用这个方法,\(O(n\log x)\),算下来是\(3\cdot 10^8\),但是 1.5s 仍然跑不出最后两个点,可能是常数过大


接下来有一种稍有优化的方法

inline int cnt2(int x){
reg int ret=0;
while(x){
ret++;
x^=(x&(-x));
}
return ret;
}

每次结果加一,然后去掉最后一个二进制中的\(1\)

复杂度和\(x\)二进制中数的个数有关,最坏也是\(O(\log x)\)

对于如何去掉的最后一个\(1\),和计算机数的储存有关

原码

原码就是一个数的二进制表示,加上符号位,符号位是\(0\)代表整数,否则是负数

但这样在表示负数时,每增加一个二进制位,数的值反而会减少,不能完成加法操作

当然也可以算它的绝对值再取符号之类的,但是对于加法这样计算机中最基础的运算,会显得太麻烦

反码

正数的反码是其本身,负数的反码是除了符号位,每一位取反的结果

这样就解决了正负数各自的加法的问题,说“各自”是因为不能跨过\(0\)

因为\(+0\)表示为\(0000\),而\(-0\)表示为\(1111\),所以运算时,每跨过一次\(0\),都会使结果少一,自己举两个例子用反码表示试试就知道

补码

于是有了补码,正数的补码还是其本身,负数的补码是它的反码加一

然后就完美的解决了加法的问题

而且\(1111\)这一位就没有数了(我们以四位二进制数为例),所以就让这一位表示\(-2^3\)

这也是为什么大部分数据类型表示的范围是\([-2^n,2^n)\)

扯完这些就能理解上面那种方法了,变成负数以后,相当于给每一位取了反,然后加一

假设这个\(x=\cdots 100\cdots\),写出来的这个\(1\)就是最后一个,也就是要去掉的\(1\),那么取反以后变成\(\cdots011\cdots\)

因为取反结果后面全是\(1\),加一,都进位,就变成了\(\cdots100\cdots\)

那么和原数做与运算,就得出了那一位\(1\),用异或去掉就行

这种方法已经能通过此题


但还有一种更妙的方法

inline int cnt3(reg int x){
x=(x&0x55555555)+((x>>1)&0x55555555);
x=(x&0x33333333)+((x>>2)&0x33333333);
x=(x&0x0f0f0f0f)+((x>>4)&0x0f0f0f0f);
x=(x&0x00ff00ff)+((x>>8)&0x00ff00ff);
x=(x&0x0000ffff)+((x>>16)&0x0000ffff);
return x;
}
inline int cnt4(reg LL x){
x=(x&0x5555555555555555ll)+((x>>1)&0x5555555555555555ll);
x=(x&0x3333333333333333ll)+((x>>2)&0x3333333333333333ll);
x=(x&0x0f0f0f0f0f0f0f0fll)+((x>>4)&0x0f0f0f0f0f0f0f0fll);
x=(x&0x00ff00ff00ff00ffll)+((x>>8)&0x00ff00ff00ff00ffll);
x=(x&0x0000ffff0000ffffll)+((x>>16)&0x0000ffff0000ffffll);
x=(x&0x00000000ffffffffll)+((x>>32)&0x00000000ffffffffll);
return x;
}

cnt3是处理\(int\)的,cnt4是处理\(long\space long\)的

可以看出,这种方法是\(O(\log\log x)\)

其实还有一种看起来更接近\(O(1)\),但是用到取模运算,所以真正跑起来可能每这个快,也比这个更难理解

以一个8为二进制数为例,\(\texttt{10111001}\),其实更多位数也一样

\(\texttt{0x55}\)的二进制是\(\texttt{01010101}\)

所以,和它与,就保留了\(1,3,5,7\)位上的\(1\),就是\(\texttt{00010001}\)

如果把这个二进制数左移一位,再和它与,那么肯定是保留了\(2,4,6,8\)为上的\(1\),然后把它分别放到了\(1,3,5,7\)位上

左移一位再与以后的结果:\(\texttt{01010100}\)

和刚才那个\(\texttt{ 00010001 }\)加完以后的结果:\(\texttt{01 10 01 01}\)

这里把它两位一断,就能很容易的发现,对于每两位来说,这两位的二进制数,就是这两位上\(1\)的个数

然后继续观察,发现\(\texttt{0x33}\)的二进制是\(\texttt{0011 0011}\)

那么\(\texttt{01 10 01 01}\operatorname{and}\texttt{00 11 00 11}=\texttt{00 10 00 01}\)

这个什么意思?当然是如果每两位分一段的话,保留\(1,3\)段中的\(1\)

同样,左移两位再与,就是保留\(2,4\)段的\(1\)并放在\(1,3\)段上

再加起来,结果就是\(\texttt{0011 0010}\)

此时,把它四位一段,前四位的二进制数是表示前四位有多少\(1\),后四位也一样

现在差不多就明白了,其实这个方法就是不断把相邻位的\(1\)的个数合并到一个更大的区间去,最后,就是整个\(x\)表示\(x\)中\(1\)的个数

返回\(x\)

然而这个比上一种方法的总时间也就快了不到半秒

放上完整代码

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
register int x=0;register int y=1;
register char c=std::getchar();
while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
return y?x:-x;
}
int n;
inline int cnt1(int x){
reg int ret=0;
while(x) ret+=x&1,x>>=1;
return ret;
}
inline int cnt2(int x){
reg int ret=0;
while(x){
ret++;
x^=(x&(-x));
}
return ret;
}
inline int cnt3(reg int x){
x=(x&0x55555555)+((x>>1)&0x55555555);
x=(x&0x33333333)+((x>>2)&0x33333333);
x=(x&0x0f0f0f0f)+((x>>4)&0x0f0f0f0f);
x=(x&0x00ff00ff)+((x>>8)&0x00ff00ff);
x=(x&0x0000ffff)+((x>>16)&0x0000ffff);
return x;
}
inline int cnt4(reg LL x){
x=(x&0x5555555555555555ll)+((x>>1)&0x5555555555555555ll);
x=(x&0x3333333333333333ll)+((x>>2)&0x3333333333333333ll);
x=(x&0x0f0f0f0f0f0f0f0fll)+((x>>4)&0x0f0f0f0f0f0f0f0fll);
x=(x&0x00ff00ff00ff00ffll)+((x>>8)&0x00ff00ff00ff00ffll);
x=(x&0x0000ffff0000ffffll)+((x>>16)&0x0000ffff0000ffffll);
x=(x&0x00000000ffffffffll)+((x>>32)&0x00000000ffffffffll);
return x;
}
int main(){
n=read();reg int a=read(),b=read(),c=read(),d=read(),x=read();
a%=d;b%=d;c%=d;x%=d;
reg int even=0,odd=0;
while(n--){
x=((1ll*a*x%d*x%d)+(1ll*b*x%d)+c)%d;
(cnt3(x)&1)?odd++:even++;
}
std::printf("%lld",1ll*odd*even);
return 0;
}

P4932 浏览器(统计二进制1的个数)的更多相关文章

  1. Luogu P4932 浏览器(二进制)

    P4932 浏览器 题意 题目背景 __stdcall在用\(Edge\)玩\(slay\)的时候,鼠标会经常失灵,这让她十分痛苦,因此她决定也要让你们感受一下\(Edge\)制造的痛苦. 题目描述 ...

  2. 洛谷——P4932 浏览器

    P4932 浏览器 __stdcall给了你n个点,第i个点有权值x[i],对于两个点u和v,如果x[u] xor x[v]的结果在二进制表示下有奇数个1,那么在u和v之间连接一个Edge,现在__s ...

  3. [LeetCode] Count Binary Substrings 统计二进制子字符串

    Give a string s, count the number of non-empty (contiguous) substrings that have the same number of ...

  4. 洛谷 P4932 浏览器 (思维题)

    题目大意:给你一个序列,求满足$x_{i}\: xor\; x_{j}$在二进制下1的数量为奇数的数对数量 打月赛的时候真没想出来,还是我太弱.. xor意义下,对于两个数,假设它们两个每一位都是2个 ...

  5. Java程序设计——反转字符串 & 找朋友 & 计算int型二进制1的个数 & 情报加密 & 计算日期 & 求近似数 & 输出较小数(练习1)

    作为刚刚入门Java的选手,其实C++的功底起到了很大的作用.但是,Java之于C++最大的不同,我个人认为,是其类的多样性.才入门的我,写着老师布置的简单的面对过程的题,如果是C++,可以算是简单了 ...

  6. 给定任意一个字符串,使用 for in 语句来统计字符出现的个数

    //找出字符串中的数字 var str = 'haj123sdk54hask33dkhalsd879'; /*function findNum(str){ var arr = []; var tmp ...

  7. vi怎么统计查找字符串的个数

    vi怎么统计查找字符串的个数 用vi打开一个比较大的文本,用vi查找指定字符串,现在怎么统计该字符串的个数呢?比如我查找ORA字符串,直接输入 /ORA的时候vi会高亮显示.现在怎么统计ORA的个数呢 ...

  8. #PHP 数组添加元素、统计数组相同元素个数、改变数组key值~_~

    一.数组添加元素 1.定义和用法: array_push() 函数向第一个参数的数组尾部添加一个或多个元素(入栈),然后返回新数组的长度. 2.语法: array_push(array,value1, ...

  9. java基础 File 递归删除文件夹中所有文件文件夹 目录(包含子目录)下的.java文件复制到e:/abc文件夹中, 并统计java文件的个数

    File 递归删除文件夹中所有文件文件夹 package com.swift.kuozhan; import java.io.File; import java.util.Scanner; /*键盘录 ...

随机推荐

  1. 2017蓝桥杯兴趣小组(C++C组)

    原题:兴趣小组 为丰富同学们的业余文化生活,某高校学生会创办了3个兴趣小组(以下称A组,B组,C组).每个小组的学生名单分别在[A.txt],[B.txt]和[C.txt]中.每个文件中存储的是学生的 ...

  2. flask入门 之 Python Shell (三)

    1.代码: #encoding:utf-8 from flask_sqlalchemy import SQLAlchemy from flask_script import Manager,Shell ...

  3. Dubbo 路由机制的实现

    Dubbo 路由机制是在服务间的调用时,通过将服务提供者按照设定的路由规则来决定调用哪一个具体的服务. 路由服务结构 Dubbo 实现路由都是通过实现 RouterFactory 接口.当前版本 du ...

  4. Python常见数据结构-Tuple元组

    Python Tuple基本特点 元组与列表类似,不同之处在于元组的元素不能修改. 与字符串和列表一样,可以根据下标进行切片索引. 元组使用小括号,单一元素的元组定义是必须加一个逗号. Python ...

  5. 操作文件-取出一个60s内log日志中ip访问次数超过100次的ip

    import timea=0while True: d={} f = open(r"/Users/**juan/Downloads/access.log",encoding=&qu ...

  6. 10.1 io流--ASCII码表

    day2.8中提到 /* * +: * 做加法运算 * * 字符参与加法运算,其实是拿字符在计算机中存储的数据值来参与运算的 * 'A' 65(B 66...) * 'a' 97(b 98...) * ...

  7. Python爬虫系列(三):requests高级耍法

    昨天,我们更多的讨论了request的基础API,让我们对它有了基础的认知.学会上一课程,我们已经能写点基本的爬虫了.但是还不够,因为,很多站点是需要登录的,在站点的各个请求之间,是需要保持回话状态的 ...

  8. Exchange 2016 OWA登陆异常

    今天,收到脚本的告警信息,有一台Exchange服务器OWA无法登陆! 手动进行了一下测试,发现确实存在问题,报错信息如下: 检查了一下该台服务器的日志,找到了如下信息 1.访问OWA的请求在HTTP ...

  9. python初学(一)

    1.输入一个百分制成绩,要求输出成绩等级A.B.C.D.E,其中90~100分为A,80~89分为B,70~79分为C,60~69分为D,60分以下为E. 要求:1)用if语句实现:2)输入百分制成绩 ...

  10. 把川普射上太阳—如何用python制作小游戏

    1.准备 开始之前,你要确保Python和pip已经成功安装在电脑上噢,如果没有,请访问这篇文章:超详细Python安装指南 进行安装. Windows环境下打开Cmd(开始—运行—CMD),苹果系统 ...