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 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
題意:
給出 ,求出所有滿足 問 是多少
分析:
這題是個很迷的題,各種神奇
首先學習到了對於取模2的冪次來說,可以進行交換(我沒找到相關定理,只是根據題解推出來的,要不然這題不可能對),什麼意思呢,就是比如一個數 ,計算 ,如果 ,那麼 ,舉個具體的例子來說
這個結論貌似只對取模2的冪有效,對於一般數的取模是肯定不對的(我沒找到相關定理解釋,但是猜測是對的,因爲這道題對了,我也是看着他們的題解)
下面先說一下這道題,很明顯我們需要得到1e9內的本原勾股數組,並且我們知道本原勾股數組公式爲:
且 互質, 有一奇一偶,即 是奇數
維基百科原話這樣說:
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的規模,而每次取模最大 ,所以預處理的時候就直接取模 好了,然後這樣預處理出每個y取模後對應下標出現了幾次
在得到每個數列a的時候,遍歷1到 ,看每個y的貢獻次數,然後在乘上這個實際取模 下標的那個a數列中的值,求和即可
而且這裏取模是用位運算做的,結論取模2的冪次的數,相當於和這個數-1按位與即
= &
最後一個神奇的地方,網上有人說法裏數列Stern-Brocot tree構造的時間複雜度是 的,不知道怎麼算,感覺這個複雜度恨不能理解,怎麼不超時呢?我用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);
}
}