hdu 1496 Equaions 题解(数论,map)

原链接:
点我QωQ

题意简述

多组数据,每次给定 a , b , c , d a,b,c,d a,b,c,d,求有多少 x 1 , x 2 , x 3 , x 4 x1,x2,x3,x4 x1,x2,x3,x4满足 a x 1 2 + b x 2 2 + c x 3 2 + d x 4 2 = 0 ax1^2+bx2^2+cx3^2+dx4^2=0 ax12+bx22+cx32+dx42=0。保证 a , b , c , d a,b,c,d a,b,c,d [ − 50 , 50 ] [-50,50] [50,50]范围内,求的 x 1 , x 2 , x 3 , x 4 x1,x2,x3,x4 x1,x2,x3,x4要在 [ − 100 , 100 ] [-100,100] [100,100]的计数。

数据

输入

多组数据,读入到文件尾。每组数据包含四个正整数 a , b , c , d a,b,c,d a,b,c,d,在 [ − 50 , 50 ] [-50,50] [50,50]范围内。(即 &gt; = − 50 , &lt; = 50 &gt;=-50,&lt;=50 >=50,<=50)。

输出

有多少 x 1 , x 2 , x 3 , x 4 x1,x2,x3,x4 x1,x2,x3,x4满足条件并且 − 100 &lt; = x 1 , x 2 , x 3 , x 4 &lt; = 100 -100&lt;=x1,x2,x3,x4&lt;=100 100<=x1,x2,x3,x4<=100

样例

输入
1 2 3 -4
1 1 1 1
输出
39088
0

思路

显然四层循环是过不去的。。。经过实践,三层也是过不去的。。。必须要 O ( 10 0 2 ) O(100^2) O(1002)
才能过,带 l o g log log都不行。。。

但是我们要先能把四层循环写出来。代码:

int ans=0;
for(int x1=-100;x1<=100;++x1)
{
    for(int x2=-100;x2<=100;++x2)
    {
        for(int x3=-100;x3<=100;++x3)
        {
            for(int x4=-100;x4<=100;++x4)
            {
                if (x1*x1*a+x2*x2*b+x3*x3*c+x4*x4*d==0)
                {
                    ++ans;
                }
            }
        }
    }
}

我们对这个代码作一些变形:

int ans=0;
for(int x1=-100;x1<=100;++x1)
{
    for(int x2=-100;x2<=100;++x2)
    {
        for(int x3=-100;x3<=100;++x3)
        {
            for(int x4=-100;x4<=100;++x4)
            {
                if (x3*x3*c+x4*x4*d==-(x1*x1*a+x2*x2*b))
                {
                    ++ans;
                }
            }
        }
    }
}

你会发现我只是移项了一下。。。
但是这样之后就能想出一个优化了。我们会发现,后面两层循环相当于求有多少 x 3 , x 4 x3,x4 x3,x4满足 c x 3 2 + d x 4 2 = − ( a x 1 2 + b x 2 2 ) cx3^2+dx4^2=-(ax1^2+bx2^2) cx32+dx42=(ax12+bx22)。然后这个东西我们珂以用一个 m a p map map记录出来。我们会轻易想到用 S T L STL STL m a p map map做。但是我没这么做,因为经过计算,会 T L E 。 。 。 TLE。。。 TLE(不是带一个特别大的 l o g log log么。。。)

想想优化。 S T L STL STL m a p map map带一个 l o g log log,但是我们其实并不需要带 l o g log log。我们能求出来的最小值是 10 0 2 × − 50 100^2\times -50 1002×50,即 − 5 × 1 0 5 -5\times10^5 5×105。我们只要开一个长度为 2 × 1 0 6 2\times 10^6 2×106的数组,整个往右滚动 1 0 6 10^6 106位,就都珂以变成正下标了,直接上数组。在清空的时候,也不要直接 m e m s e t memset memset,而是反向进行计算的操作( + + + − - ),这样就珂以保证任何计算都是 O ( 10 0 2 ) O(100^2) O(1002)的了,也就珂以过了。

代码:

#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
    #define int long long
    #define N 1001000
    struct Map//更快的Map(不带log)
    {
        int v[(N<<1)+100];
        void clear()
        {
            memset(v,0,sizeof(v));
        }
        int& operator[](int i)//重载[]运算符
        {
            return *(v+i+N);
            //取值的时候向右滚动
        }
    };
    Map rec2;
    //rec2[i]表示x3*x3*c+x4*x4*d==i的有多少
    int a,b;
    int c,d;
    void Build()
    {
        for(int i=-100;i<=100;++i)
        {
            for(int j=-100;j<=100;++j)
            {
                if (i and j)//都不能是0
                {
                    ++rec2[i*i*c+j*j*d];
                }
            }
        }
    }

    void Solve()
    {
        int ans=0;
        for(int i=-100;i<=100;++i)
        {
            for(int j=-100;j<=100;++j)
            {
                if (i and j)
                {
                    ans+=rec2[-(i*i*a+j*j*b)];
                    //记录答案
                }
            }
        }
        printf("%lld\n",ans);
    }

    void Clear()
    {
        for(int i=-100;i<=100;++i)
        {
            for(int j=-100;j<=100;++j)
            {
                if (i and j)
                {
                    rec2[i*i*c+j*j*d]--;
                    //清空
                }
            }
        }
    }
    void Main()
    {
        if (0)
        {
            freopen("","r",stdin);
            freopen("","w",stdout);
        }
        while(scanf("%lld%lld%lld%lld",&a,&b,&c,&d)==4)
        {
            Build();
            Solve();
            Clear();
        }
    }
    #undef int //long long
};
int main()
{
    Flandle_Scarlet::Main();
    return 0;
}

回到总题解界面

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值