problem H:异或密码
晨晨在纸上写了一个长度为N的非负整数序列{aiai}。对于这个序列的一个连续子序列{al,al+1,…,aral,al+1,…,ar}晨晨可以求出其中所有数异或的结果 al xor al+1xor…xor ar其 中xor表示位异或运算,对应C、C++、 Java等语言中的^运算。
小璐提出了M个询问,每个询问用一个整数 xixi描述。
对于每个询问,晨晨需要找到序列{ai}的所有连续子序列,求出每个子序列异或的结果,找到所有的结果中与 xi之差的绝对值最小的一个,并告诉小璐相应子序列的长度。
若有多个满足条件的连续子序列,则告诉小璐这些子序列中最长的长度。
Input
包含多组测试数据,第一行一个正整数T,表示数据组数。
每组数据共两行。
第一行包含N+1个非负整数。其中第一个数为N,表示序列的长度;接下来N 个数,依次描述序列{ aiai}中的每个数。
第二行包含M+1个整数。其中第一个数为M,表示询问的个数;接下来M个数 xixi,每个数对应题目描述中的一个询问。
保证 1 <= N <= 100,1 <= M <= 100,ai <= 1024,|xi| <= 1024,数据组数 <= 100。
Output
对于每组数据输出M + 1行。前M行对应晨晨M个询问的回答,第M + 1行为空行
Sample Input
2
2 1 1
2 0 2
3 1 2 4
3 10 5 1
Sample Output
2
1
3
2
1
本题就是找出所有的连续子序列,经推算可知有n*(n+1)/2个上升的子序列,可以得出要存储的数组的大小。又根据题意N的范围为一百,直接暴力求解即可。
AC代码如下:
#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
int a[105];
int b[105];
int sum[5055];
int len[5055];
int main()
{
int n, m1;
int t;
cin >> t;
while (t--)
{
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
memset(sum, 0, sizeof(sum));
memset(len, 0, sizeof(len));
int x = 0;
int m;
cin >> n;
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
cin >> m1;
for (int i = 0; i < m1; i++)
cin >> b[i];
for (int i = 0; i < n; i++)
{
m = 0;
for (int j = i; j < n; j++)
{
m ^= a[j];
sum[x] = m;//依次存储当前序列的异或值
len[x] = j - i + 1;//下标相减加一即为长度(存储长度)
x++;
}
}
/* for (int i = 0; i < x; i++)
{
cout << "*" << sum[i] << "*";
}
cout << endl;*/
for (int i = 0; i < m1; i++)
{
int inf = 9999999, lena = 0;
for (int j = 0; j <x; j++)
{
if (inf >= abs(b[i] - sum[j]))
{
if (inf == abs(b[i] - sum[j]))
{
lena = max(lena, len[j]);//如果值相等则返回最长的长度
}
else{
inf = abs(b[i] - sum[j]);//否则的话改变inf的大小
lena = len[j];//将长度改为当前长度
}
}
}
cout << lena << endl;
}
cout << endl;
}
return 0;
}
HDU5969 最大的位或:
B君和G君聊天的时候想到了如下的问题。
给定自然数l和r ,选取2个整数x,y满足l <= x <= y <= r ,使得x|y最大。
其中|表示按位或,即C、 C++、 Java中的|运算。
Input
包含至多10001组测试数据。
第一行有一个正整数,表示数据的组数。
接下来每一行表示一组数据,包含两个整数l,r。
保证 0 <= l <= r <= 10^18。
Output
对于每组数据输出一行,表示最大的位或。
Sample Input
5
1 10
0 1
1023 1024
233 322
1000000000000000000 1000000000000000000
Sample Output
15
1
2047
511
1000000000000000000
本题思路:就是要求最大的位或我们可以从二进制角度出发,根据或运算的性质位与位进行比较如二进制:
101100111011
100110011101
从前往后找到第一个位互不相同的,再将其后面的位都化为一即为最大的位或,化简为如下形式:
ans:101111111111即为最后的答案。
AC代码如下有以下两种形式:
(依次化为二进制再一个一个比较(找到不同的再化为一),再将二进制化为十进制(相对麻烦))。
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
ll a[100];
ll b[100];
ll c[100];
int main()
{
int t;
cin >> t;
while (t--)
{
memset(a, 0, sizeof(a));
memset(b, 0, sizeof(b));
memset(c, 0, sizeof(c));
long long x, y;
cin >> x >> y;
if (x == y)
{
cout << y << endl;
continue;
}
int l = 1;
while (x)
{
a[l++] = x % 2;//xiaode
x /= 2;
}
ll m = 1;
while (y)
{
b[m++] = y % 2;//大的
y /= 2;
}
for (int i = m - 1; i >l - 1; i--)
{
a[i] = 0;
}
/*for (int i = 1; i <= m - 1; i++)
cout <<"*"<< b[i] << "*";*/
ll i1;
for (i1 = m - 1; i1 >= 1; i1--)
{
c[i1] = b[i1];
if (a[i1] != b[i1])
{
c[i1] = 1;
for (i1; i1 >= 1; i1--)
{
c[i1] = 1;
}
break;
}
}
long long sum = c[1];
for (ll j = 2; j <= m - 1; j++)
{
long long s1 = 1;
for (ll i = 1; i <j; i++)
s1*=2;
sum += c[j] * s1;
}
cout << sum << endl;
}
return 0;
}
(long long有63位直接通过右移63位逐步开始找,直到找到了不相同的保存当前位数)
#include<iostream>
using namespace std;
int main()
{
long long L, r;
int t;
cin >> t;
while (t--)
{
cin >> L >> r;
long long i = 63;
while (i >=0 && (r >> i) == (L>> i))i--;//依次比较高位是否相同从最高位64位开始比较
long long x = (((long long)1 << (i + 1)) - 1);//找到了右移i+1位再减个一相或就是最大值。
cout << (r | x) << endl;
}
return 0;
}