hdu2017青岛网络赛Pythagoras(Tree of primitive Pythagorean triples)

来源:互联网 发布:linux arp命令详解 编辑:程序博客网 时间:2024/05/21 17:06

题面:

Given a list of integers a0,a1,a2,,a2k1. 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 and they are relatively prime, i.e., have no common divisor larger than 1.
思维过程:

感觉求出所有的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)。

好了我们可以列出如下几个矩阵:

{\begin{array}{lcr}A={\begin{bmatrix}1&-2&2\\2&-1&2\\2&-2&3\end{bmatrix}}&B={\begin{bmatrix}1&2&2\\2&1&2\\2&2&3\end{bmatrix}}&C={\begin{bmatrix}-1&2&2\\-2&1&2\\-2&2&3\end{bmatrix}}\end{array}}

对应于上面说的A1,A2,A3各自的逆。

但是实际上还有别的矩阵组也可以。。。

{\begin{array}{lcr}A'={\begin{bmatrix}2&1&-1\\-2&2&2\\-2&1&3\end{bmatrix}}&B'={\begin{bmatrix}2&1&1\\2&-2&2\\2&-1&3\end{bmatrix}}&C'={\begin{bmatrix}2&-1&1\\2&2&2\\2&1&3\end{bmatrix}}\end{array}}

第一个可以从结点(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;
}


原创粉丝点击