给出k和 这个数,求所有勾股数三元组(x, y, z) (x
不得不说我是完完全全的数论菜鸟,只会孙子定理的那种233。这道题真的是让我学会了好多东西。
在看思路之前,你需要了解如下知识。
假设两数为a和b。则用一个表达式
(a-b)&1
即可完成。由于采用了位运算,所以效率很高。
原理:两数奇偶性相同,相减为偶数。两数奇偶性不同,相减为奇数。故两数做减法。&运算符为按位与,奇数的最低为是1,偶数是0。负数的补码一样适用这个规律。(自己开个计算器就懂了)
话不多说,直接上代码。代码中m为被求,数组p里存访m的所有质因数。
for (int q=2; q*q<=m; q++) {
if (!(m%q)) {
p[cnt++] = q;
while (!(m%q)) m/=q; //注意这里
}
}
if (m != 1) p[cnt++] = m;
注意while语句。这种操作可以用O(logn)的时间复杂度消去m的所有已经算出的质因子。比如说64,找出了质因子2之后,m就立即变成1了。当然之后要“捡漏”。
这里采用位运算。
t&MOD
直接一个表达式解决所有问题。其中MOD为2的n次方减1。采用位运算可以写成
int MOD = (1 << n) - 1;
(不懂的话自己开个计算器秒懂233)
两个数互质,就是两个数没有大于1的公因子。只要判断前一个数是否能整除后一个数所有的质因子即可,反之亦然。这里就不上代码了。
我们很容易想到如下方法。
for (int i=1; i<=n; i++) {
int m = a + i*i;
}
如下方法也能达到同样的效果。
int c = a;
for (int j=1; j
这种方法把多次运算的乘法,换成了累加。至于原理,大家算一遍就会明白了。优化常数阶。
------------------------------------------------华丽的分割线------------------------------------------------
这里采用预先处理打表的方法。
因为勾股数公式为x^2 + y^2 = z^2,这里z小于1e9(题目要求),所以可得x^2小于1e9(因为勾股数前两个一定小于最后一个)。
勾股数生成公式:x=i*i-j*j, y=2*i*j, z=i*i+j*j 其中i和j为互质且奇偶性相反的正整数。
所以枚举i从1到,接着计算x的所有质因数。接着从1到i枚举j,这里有两个剪枝:第一个是判断勾股数第三个数z是否大于1e9。第二个为判断奇偶性是否相反。
接着判断i和j是否互质,如果是的话,就把i*i-j*j和2*i*j也就是x和y中较大的那个加入备选数组,使数组自增1。
等等,这里数可能很大(到1e9),数组开不下。这里又用到了一个优化:题目总数据量只有2的17次方,为131072。那我们先把这个数模2^17,加入数组即可。这样做的话,因为题目的实际范围使小于等于2的17次方的,比如说a mod 2^17 mod 2^4,结果和a mod 2^4是相同的。(还是开一下计算器就明白了,要用二进制的思想哦)正好还免去了重复打表的过程,一举两得。
以上是打表过程,需要预先操作。然后读入数据,此时备选数组中的内容就是次数。所以计算
long long sum = 0;
for (int i=0; i<=(1<<17)-1; i++) {
sum += (long long)a[i&((1<
,其中a为题目所给数据,b为备选数组,累加即可,注意long long防止越界。
碎碎念:终于写完了,也是半夜进入新的一天了,明天还要上课,滚去睡觉了。
#include
#include
typedef long long LL;
const int MAXN = (1 << 17);
const int MOD = (1 << 17) - 1;
int A[MAXN+5];
int K;
int B[MAXN+5];
void pre() {
int sqrt1e9 = sqrt(1000000000);
for (int i=1; i<=sqrt1e9; i++) {
int m=i;
int p[20];
int cnt = 0;
//计算质因数
int q;
for (q=2; q*q<=m; q++) {
if (!(m%q)) {
p[cnt++] = q;
while (!(m%q)) m/=q;
}
}
if (m != 1) p[cnt++] = m;
int c = i*i;
for (int j=1; j1000000000) break;
if (!((i-j)&1)) continue; //判断奇偶性
int k;
for (k=0; k= cnt) {
int t = 2*i*j;
if (i*i-j*j > t) t = i*i-j*j;
B[t&MOD]++;
}
}
}
}
void solve() {
int t = (1 << K) - 1;
LL sum = 0;
for (int i=0; i<=MOD; i++) {
sum += (LL)A[i&t]*B[i];
}
printf("%lld\n", sum);
}
int main() {
pre();
int T;
scanf("%d", &T);
while (T--) {
scanf("%d", &K);
for (int i=0; i<(1 << K); i++) {
scanf("%d", &A[i]);
}
solve();
}
return 0;
}