数论——异或,两道水题。

第一题:(没有链接)

题目大意:给你n个数(n <= 1000000),第i个数用ai表示(每个a <= 1000000),求出每个数与其之后的数的xor和。

举个例子吧,比如三个数1 2 3答案就应该为1 xor 2 + 1 xor 3 + 2 xor 3 = 4;

题解:

首先有一个O(n*n)的算法,就是暴力枚举,可以过40%数据。

程序大体:

for (int i = 1;i < n;i ++)
    for (int j = i + 1;j <= n;j ++) ans += a[i] ^ a[j];

很明显TLE,考虑一下两个数的二进制的某一位xor能产生贡献的条件是什么?就是两位不同。。。 也就是其中一个是1,另一个是0二进制下的这一位就可以产生贡献。咦?好像有很大的用。那么我们可以很容易的想到乘法原理。。。怎么用呢,用cnt[i]表示n个数在二进制下的第i为1的有多少个,那么为0的自然就是n - cnt个,我们可以这样考虑,让每一个这一位为1的和每一个这一位为0的进行xor,这样是可以产生贡献的。然后就用cnt * (n - cnt)种搭配能让这一位产生贡献(很明显这些搭配没有重复),那么只要让cnt * (n - cnt)乘以1 << k -1(k表示当前是第几个二进制位)就可以得到第k位的和了。

代码如下:

#include <cstdio>
#include <iostream>

using namespace std;

int n;
long long ans, a;
long long cnt[30];

int main(){
    scanf("%d", &n);
    for (int i = 1;i <= n;i ++){
        scanf("%lld", &a);
        for (int i = 1;i <= 20;i ++) if (1 << (i-1) & a) cnt[i]++;
    }
    for (int i = 1;i <= 20;i ++) ans += cnt[i] * (n-cnt[i]) * (1 << i-1);
    printf("%lld", ans);
}
//tips:别忘了用long long

第二题:题目链接

题目大意:给你n个数(n <= 1e5),每个数为ai(每一个a <= 1e9)求每个区间xor值的和,这里吐槽一下,说的数据是1e9实际只有2的16次方的数据(洛谷第一个讨论看见的)

举个例子吧,比如两个数1 2,你需要输出的就是1 + 2 + 1 xor 2 = 6,单独的一个数也叫一个区间。

题解:同样的暴力枚举每一区间复杂度O(n*n),TLE。。。做这道题需要知道若a xor b = c, 那么a xor c = b;(我不会证明,这是xor的运算性质),然后我们同样的向二进制方向想,对于每一位产生贡献进行考虑,若[l, r]这个区间对于这一位能产生贡献的条件就是其中1的个数为奇数且0的个数为偶数,或者0的个数为奇数且1的个数为偶数。那么我们可以求出xor的前缀也就是sum[i]表示前i个数的异或值。那么统计答案只需要求出对于二进制下的每一位这n个前缀中有几个1,用cnt表示,有几个0(也就是n - cnt)然后让每一个1与每一个0组合形成一个区间然后统计就是cnt (n-cnt) 1 << (k -1);

但是这样是错的,你会发现你让他们组合了却没有考虑到1-X这样的区间也就是让还有cnt个也可以产生贡献。所以答案统计应该是(cnt (n - cnt) + cnt) 1 << (k - 1)也就是cnt (n - cnt + 1) 1 <<(k -1);

代码:

#include <cstdio>
#include <iostream>

const int maxn = 100010;

using namespace std;

int n;
long long ans;
long long sum[maxn];

int main(){
    scanf("%d", &n);
    for (int i = 1;i <= n;i ++) scanf("%lld", &sum[i]), sum[i] = sum[i-1] ^ sum[i];
    for (int i = 1;i <= 17;i ++){
        long long cnt = 0;
        for (int j = 1;j <= n;j ++) if (sum[j] & (1 << (i-1))) cnt ++;
        ans += cnt * (n - cnt + 1) * (1 << i - 1);
    }
    printf("%lld", ans);
    return 0;
}
//tips:别忘了开long long

蒟蒻开始跑路。。。

时间: 2025-01-11 10:11:50

数论——异或,两道水题。的相关文章

两道水题

昨天同桌为我推荐了p1904这道水题,然后他就写不出来了--本来不想写,但是看他写得很麻烦,为了给他展示我的代码能力就写了一下. 即使类型为"其他",但还是掩盖不了模拟的事实.那么直接sort Ai,再在前k个牛中找到Bi最高的就行.复杂度为N*logN+k #include<iostream> #include<iomanip> #include<cstdio> #include<algorithm> using namespace s

Co-prime Array&amp;&amp;Seating On Bus(两道水题)

Co-prime Array Time Limit:1000MS     Memory Limit:262144KB     64bit IO Format:%I64d & %I64u Submit Status Practice CodeForces 660A Description You are given an array of n elements, you must make it a co-prime array in as few moves as possible. In ea

两道水题(月之数)(排序)

月之数 Time Limit : 1000/1000ms (Java/Other)   Memory Limit : 32768/32768K (Java/Other) Total Submission(s) : 32   Accepted Submission(s) : 22 Font: Times New Roman | Verdana | Georgia Font Size: ← → Problem Description 当寒月还在读大一的时候,他在一本武林秘籍中(据后来考证,估计是计算

POJ百道水题列表

以下是poj百道水题,新手可以考虑从这里刷起 搜索1002 Fire Net1004 Anagrams by Stack1005 Jugs1008 Gnome Tetravex1091 Knight Moves1101 Gamblers1204 Additive equations 1221 Risk1230 Legendary Pokemon1249 Pushing Boxes 1364 Machine Schedule1368 BOAT1406 Jungle Roads1411 Annive

刷了500道水题是什么体验?

并没有什么卵用. 我马上大二了,大一两学期目测切了1000道水题了,毫无意义. 至今不理解kmp和后缀数组,只会模板.数论和博弈论是什么?能吃吗?只会打表.图论至今不会tarjan,话说dlx是什么?插头dp,这是什么?数据结构还好,经常做高中生的题,可持久化可持久化线段树也能花一下午时间写出来,然而并不会考. 平时做题只刷水题,遇到难题的时候,随手搜题解,看了看,哇,这居然能这么搞!然后抄一遍别人代码,交上去ac. cf一年几乎没缺过,花了大一上半年时间才滚上div1.然而至今紫号一堆,黄名一

大数--几道水题,用来学学JAVA

几道水题,练习一下JAVA写大数 poj2305  Basic remains 大数可以直接用非十进制读入,读入的数在变量中是十进制的 输出的时候要先用BigInteger的toString方法转换为相应的进制 1 import java.math.*; 2 import java.util.*; 3 class Main 4 { 5 public static void main(String[] args) 6 { 7 Scanner cin = new Scanner(System.in)

逛园子,看到个练习题,小试了一把(淘宝ued的两道小题)

闲来无事,逛园子,充充电.发现了一个挺有意思的博文,自己玩了一把. 第一题:使用 HTML+CSS 实现如图布局,border-widht 1px,一个格子大小是 60*60,hover时候边框变为橘红色(兼容IE6+,考虑语义化的结构) 效果图: 简单分析一下: 使用伪类 :hover的时候相对定位 改变z-index, 代码如下: 1 <!DOCTYPE html> 2 <html lang="en"> 3 <head> 4 <meta c

1503171912-ny-一道水题

一道水题 时间限制:1000 ms  |  内存限制:65535 KB 难度:2 描写叙述 今天LZQ在玩一种小游戏,可是这游戏数有一点点的大,他一个人玩的累.想多拉一些人进来帮帮他.你能写一个程序帮帮他吗?这个游戏是这种:有一行数字,假设我们把这行数字中的'5'都看成空格,那么就得到一行用空格切割的若干非负整数(可能有些整数以'0'开头,这些头部的'0'应该被忽略掉,除非这个整数就是由若干个'0'组成的,这时这个整数就是0). 你的任务是:对这些切割得到的整数,依从小到大的顺序排序输出,大家赶

(字典树3道水题)codeforces 665E&amp;282E&amp;514C

665E 题意: 给一个数列和一个整数k,求这个数列中异或起来大于等于k的子串数量. 分析: 其实只要维护一个维护前缀和就行了,把前缀和加到字典树里,然后递归search一下,注意需要剪枝,不然会T, if(s + (1ll << (i + 1)) - 1 < k)return 0; 这句话的意思是如果后面的二进制全都是1,都达不到k,就不用继续递归了. 代码: 1 #include <cstdio> 2 #include <cstring> 3 #include