题面:
感觉求出所有的y是必须的,设y的集合为A。求出y后就可以O(num(A))的解决问题了。同时又至少要O(num(A))的时间复杂度,所以决定总复杂度的是求出A集合的算法复杂度,我们要思考的就是如何更快的求出集合A。
解法:
先考虑一些引理。
引理一:
本原勾股数都可表示为如下三元组(2*u*v,u*u-v*v,u*u+v*v)。
证明:显然,或者说基础。
引理二:
存在这样的矩阵A,A可逆,且存在无穷多个满足x*x+y*y=z*z的向量(x,y,z)A左乘该向量得到的新向量(x1,y1,z1)也满足x1*x1+y1*y1=z1*z1,并且z1<=z。设这样的矩阵A集为U。
证明:设矩阵A的各种参数去构造矩阵A,随便构造就可以发现好几个。
推论:
A的逆也满足以上除了(z1<=z)的性质,即z1>z。
引理二:
在上面的U集中一定可以找到三个元素A1,A2,A3,使得对于每一个勾股三元组,一定在A1,A2,A3中有且只有一个使得左乘该三元组,能得到新的勾股三元组。
证明:额。。。略。。。论文里应该有的。。。
推论:
可以用A1,A2,A3各自的逆构造出所有的本原勾股数三元组。
证明:
z的单调不增性,且Ai的不动点唯一。
wiki链接(https://en.wikipedia.org/wiki/Tree_of_primitive_Pythagorean_triples)。
好了我们可以列出如下几个矩阵:
对应于上面说的A1,A2,A3各自的逆。
但是实际上还有别的矩阵组也可以。。。
第一个可以从结点(3,4,5)用bfs的方式构建一颗树,这个树名就叫做Tree of primitive Pythagorean triples
因此我们找到了求A集合最好的方法。
代码:
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long ll;
const int man=1e9;
const int Mod=(1<<17)-1;
int change[Mod];
void dfs(ll x,ll y,ll z){
if (z>man) {
return;
}
change[max(x,y)&Mod]++;
ll xx=x<<1;
ll yy=y<<1;
ll zz=z<<1;
dfs(x-yy+zz, xx-y+zz, xx-yy+zz+z);
dfs(x+yy+zz, xx+y+zz, xx+yy+zz+z);
dfs(yy+zz-x, y+zz-xx, yy+zz+z-xx);
}
int main(){
int t;
cin>>t;
dfs(3, 4, 5);
while (t--) {
int k;
cin>>k;
int mod=1<<k;
ll sum=0;
int a;
int j;
for (int i=0; i<mod; i++) {
scanf("%d",&a);
j=i;
while (j<=Mod) {
sum+=change[j]*a;
j+=mod;
}
}
printf("%lld\n",sum);
}
return 0;
}