原题链接:Codeforces 424C - Magic Formulas(大家自行把链接头的https改成http)
题目大意:题目已经说得很清楚了,就是给定p1, p2, ..., pn,求Q
大致思路:异或操作满足交换律,所以 Q = q1^q2^.......^qn =(p1^p2......^pn) ^ [ ((1%1)^....^(1%n)) ^ ((2%1)^......^(2%n)) ^.... ^((n%1)^....^(n%n)) ] 。其中红色部分可直接在读入数据时完成,关键在于绿色部分的处理。 绿色部分其实可以转化成矩阵:
0 1%2 ........... 1%n
0 2%2 ............ 2%n
0 3%2 ............ 3%n
.....................................
0 n%2 ............. n%n
第一列全部是0,全部求异或的结果也是0。第二列开始就有规律了,每一列都以列号i作为循环周期,比如第二列为1、0、1、0、1……,周期为2;第三列为1、2、0、1、2、0、1……,周期为3。根据异或的规律,每个数字出现两次的话,异或结果即为0。所以我们只要计算每列以2*i为周期循环剩下的那几个数即最后的 n%(2*i) 个数的异或即可,具体实现如下:
#include <iostream>
#include <cstdio>
using namespace std;
int cal(int n, int order) {
int temp = n%(order*2);
int res = 0, j=1;
for(int i=0; i<temp; i++) {
res ^= j;
j++;
if(j==order) j=0; //实际每列以order为周期,每个周期最后一个数为0,所以需要重置一次
}
return res;
}
int main() {
int n, x;
int res = 0;
scanf("%d%*c", &n);
for(int i=1; i<=n; i++) {
scanf("%d", &x);
res ^= x;
if(i>=2) res ^= cal(n,i);
}
cout<<res<<endl;
return 0;
}
跑一遍发现超时了,很显然cal函数不能放在循环里,该怎么改? 其实,如果再仔细观察一下就会发现每次cal函数循环部分计算的内容是递增形式的,比如有时是1^2^3,有时可能就是1^2^3^4,每次计算的必定是连续数字的异或,所以这些计算的结果可以事先计算好,这样就不用在主函数循环内再嵌套一层循环了,具体实现代码:#include <iostream>
#include <cstdio>
using namespace std;
int m[1000005];
int main() {
int n, x;
int res = 0;
m[0] = 0;
scanf("%d%*c", &n);
for(int i=1; i<=n; i++)
m[i] = m[i-1] ^ i;
for(int i=1; i<=n; i++) {
scanf("%d", &x);
res ^= x;
if((n/i)%2==1) res ^= m[i-1];
res ^= m[n%i];
}
cout<<res<<endl;
return 0;
}
修改后的代码中,使用数组m事先计算好后面要用到的异或结果。具体的使用在 19、20 行:先判断是否有奇数个大小为 i 的周期(偶数个异或的结果就为0了),如果有就“加上”一个周期的异或结果;然后再“加上”不满一个周期部分的异或结果。