Pythagoras HDU - 6211
Given a list of integers a0,a1,a2,⋯,a2k−1. Pythagoras triples over 109 are all solutions of x2+y2=z2 where x,y and z are constrained to be positive integers less than or equal to 109. You are to compute the sum of ay mod 2k of triples (x,y,z) such that
x<y<z
x
<
y
<
z
and they are relatively prime, i.e., have no common divisor larger than 1
.
Input
The first line is an integer T (1≤T≤3) indicating the total number of cases.
For each test case the first line is the integer k (1≤k≤17).
The second line contains 2k integers corresponding to a0 to a2k−1, where each ai satisfies 1≤ai≤255
.
Output
For each case output the sum of ay mod 2k
in a line.
Sample Input
3
2
0 0 0 1
2
1 0 0 0
2
1 1 1 1
Sample Output
39788763
79577506
159154994
题意:
给出 2k个数ai 2 k 个 数 a i ,求出所有满足 (x,y,z)为本原勾股数组且x<y<z<1e9 ( x , y , z ) 为 本 原 勾 股 数 组 且 x < y < z < 1 e 9 问 ∑ay%2k ∑ a y % 2 k 是多少
分析:
这题是个很迷的题,各种神奇
首先学习到了对于取模2的幂次来说,可以进行交换(我没找到相关定理,只是根据题解推出来的,要不然这题不可能对),什么意思呢,就是比如一个数 n n ,计算,如果 k1>k2 k 1 > k 2 ,那么 n%2k1%2k2=n%2k2 n % 2 k 1 % 2 k 2 = n % 2 k 2 ,举个具体的例子来说 9%8%2=1=9%2=1 9 % 8 % 2 = 1 = 9 % 2 = 1
这个结论貌似只对取模2的幂有效,对于一般数的取模是肯定不对的(我没找到相关定理解释,但是猜测是对的,因为这道题对了,我也是看着他们的题解)
下面先说一下这道题,很明显我们需要得到1e9内的本原勾股数组,并且我们知道本原勾股数组公式为:
且 m,n m , n 互质, m,n m , n 有一奇一偶,即 m−n m − n 是奇数
维基百科原话这样说:
Euclid’s formula[3] is a fundamental formula for generating Pythagorean triples given an arbitrary pair of integers m and n with m > n > 0. The formula states that the integers
form a Pythagorean triple. The triple generated by Euclid’s formula is primitive if and only if m and n are coprime and not both odd. When both m and n are odd, then a, b, and c will be even, and the triple will not be primitive; however, dividing a, b, and c by 2 will yield a primitive triple when m and n are coprime and both odd.[4]
因此目标是枚举出互质的数对,然后判断m-n是奇数那么就找到了一个勾股数
枚举互质的数对这时有一个神奇的方法,就是法里数列,构造法里数列的方式以一个叫Stern-Brocot tree的东西,其实就是递归生成n阶的法里数列,法里数列有非常良好的性质,其中一个就是得到的分数分子分母都是互质的,而且是全部的n内的互质对构成的真分数(具体关于法里数列的内容去网上找吧我这里有有个法里数列csdn,可能不全,可以看看维基百科),这样就能得到n内互质数对,然后判断就能得到本原勾股数组
根据上面我们说的取模2的幂次的性质,因为y大小会达到接近1e9的规模,而每次取模最大 217 2 17 ,所以预处理的时候就直接取模 217 2 17 好了,然后这样预处理出每个y取模后对应下标出现了几次
在得到每个数列a的时候,遍历1到 217 2 17 ,看每个y的贡献次数,然后在乘上这个实际取模 2k 2 k 下标的那个a数列中的值,求和即可
而且这里取模是用位运算做的,结论取模2的幂次的数,相当于和这个数-1按位与即
mod 2k
m
o
d
2
k
=
n
n
&
最后一个神奇的地方,网上有人说法里数列Stern-Brocot tree构造的时间复杂度是 O(n2) O ( n 2 ) 的,不知道怎么算,感觉这个复杂度恨不能理解,怎么不超时呢?我用clcok计算了一下时间花费发现1e5的时候0ms,1e8的时候500多毫秒,1e9的时候就栈溢出了,所以本地跑不出来,你也测不了样例,提交就是过,测评姬真强,哈 哈。。。
code:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1 << 17;
const int MAX = 1e9;
int k;
int a[maxn+50];
int ans[maxn+50];
void solve(int l1,int r1,int l2,int r2){
int ml = (l1 + l2);
int mr = (r1 + r2);
if((ll)ml * ml + (ll)mr * mr > MAX) return;
if((mr - ml) & 1){
ans[max(mr*mr-ml*ml,2*ml*mr)&(maxn-1)]++;
}
solve(l1,r1,ml,mr);
solve(ml,mr,l2,r2);
}
int main(){
int T;
scanf("%d",&T);
solve(0,1,1,1);
while(T--){
scanf("%d",&k);
for(int i = 0; i < (1 << k); i++){
scanf("%d",&a[i]);
}
ll sum = 0;
for(int i = 0; i < maxn; i++){
sum += (ll)ans[i] * a[i&(1<<k)-1];
}
printf("%lld\n",sum);
}
return 0;
}
此外这题还以一种方法来求本原勾股数组,真是打开眼界
利用这个数来生成Tree of primitive Pythagorean triples
也是递归,其实就是给了三个矩阵,每个本原勾股数组乘这三个 矩阵都会生成三个新的本原勾股数组,而且这样递归下去不会重复,那么让根节点为(3,4,5),一直往下递归求即可,其实就是个三叉树,当然了本地必然还是跑不出来的,别想了1e9呢
#include<bits/stdc++.h>
using namespace std;
const int LMT = 1e9;
long long cnt = 0;
const int N = 1<<17;
const int MOD = N - 1;
int dig[N], a[N], T, k;
//核心算法
void solve(long long a, long long b, long long c)
{
if(c > LMT) return;
dig[ max(a, b)&MOD ] ++;
long long aa = a<<1;
long long bb = b<<1;
long long cc = c<<1;
solve(a-bb+cc, aa-b+cc, aa-bb+cc+c);
//solve(a-(b<<1)+(c<<1), (a<<1)-b+(c<<1), (a<<1)-(b<<1)+(c<<1)+c);
solve(a+bb+cc, aa+b+cc, aa+bb+cc+c);
//solve(a+(b<<1)+(c<<1), (a<<1)+b+(c<<1), (a<<1)+(b<<1)+(c<<1)+c);
solve(bb+cc-a, b+cc-aa, bb+cc+c-aa);
//solve(-a+(b<<1)+(c<<1), -(a<<1)+b+(c<<1), -(a<<1)+(b<<1)+(c<<1)+c);
}
int main()
{
solve(3, 4, 5);
scanf("%d", &T);
while(T-- && scanf("%d", &k)!=EOF)
{
int mod = (1<<k);
for(int i=0;i<mod;i++)
scanf("%d", &a[i]);
long long ans = 0;
for(int i=0, j=0;i<N;i++,j++)
{
if(j == mod) j = 0;
ans += dig[i] * a[j];
}
printf("%lld\n", ans);
}
}