[SMOJ1812]解方程

97 篇文章 0 订阅
16 篇文章 0 订阅

题目描述

有 5 个整数 a,b,c,d,e ,均在 [50,50] 中,求满足

a×x31+b×x32+c×x33+d×x34+e×x35=0
的正整数组合 {x1,x2,x3,x4,x5} 的个数。
其中任意的 x 不能等于0。

输入格式 1812.in

一行 5 个数,分别是 a,b,c,d,e

输出格式 1812.out

一个整数表示正整数组合的个数。

输入样例 1812.in

37 29 41 43 47

输出样例 1812.out

654


既然是要解方程,给定了系数,最显然(但也是最暴力)的做法就是枚举每一个 x ,然后检查、计数。时间复杂度显然是 O(n5) 的,本题中每个 x 的取值范围都有 100,那么就是 1010,显然是不可行的。

进一步思考,如果将方程变形得到

a×x31+b×x32+c×x33+d×x34=e×x35
其实就可以只枚举前四个 x ,进而确定 x5。但是还要开立方,比较麻烦,不过 x 比较小,可以通过预处理一个 bool 数组,pow3[i] 标记是否有某个数的立方为 i,这样就可以实现 O(1) 判断,总的复杂度是 108 左右,似乎是可行的。

不过,这样还是很危险。
其实我们完全可以利用类似的思想,还是将方程变形,得到

a×x31+b×x32+c×x33=(d×x34+e×x35)
如果我们能枚举前三个 x ,而且快速判断是否有 x4 x5 满足方程,就可以在 106 的时间内完成任务。关键在于,方程左边代入计算得到一个值之后,取它的相反数,如何判断是否有 x4 x5 能得到它呢?

预处理嘛。
先枚举 x4 x5 ,算出所有的 d×x34+e×x35 ,hash 保存起来,再去枚举 x1 x2 x3 ,在 hash 里面检查是否有满足要求的值即可。
但是要注意一个问题,这题可能会出现负数,因此要 hash 处理的时候要统一先加上一个大的偏移量。

由这题的不断优化我们应该归纳出一类方法,即分解任务的思想,在题目有一些限制的时候,尝试是否可以把它们拆分解决,有时候可以取得很好的效果。

参考代码:

#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>

using namespace std;

const int prime = 23333;

struct Data {
    int value, count;
    Data () { value = count = 0; }
} hash[prime];

int a1, a2, a3, a4, a5;

void insert(int x) {
    x += 23333333; //偏移量,使 hash 值变成非负数
    int y = x % prime;
    while (hash[y].value != x && hash[y].count) {
        y += x % 1007 + 1;
        if (y >= prime) y -= prime;
    }
    hash[y].value = x;
    ++hash[y].count;
}

int find(int x) {
    x += 23333333;
    int y = x % prime;
    while (hash[y].value != x && hash[y].count) {
        y += x % 1007 + 1;
        if (y >= prime) y -= prime;
    }
    return hash[y].count;
}

int main(void) {
    freopen("1812.in", "r", stdin);
    freopen("1812.out", "w", stdout);
    scanf("%d%d%d%d%d", &a1, &a2, &a3, &a4, &a5);
    for (int x4 = -50; x4 <= 50; x4++)
        if (x4) //注意 x 均不能为 0,下同
            for (int x5 = -50; x5 <= 50; x5++)
                if (x5) insert(a4 * x4 * x4 * x4 + a5 * x5 * x5 * x5);
    int ans = 0;
    for (int x1 = -50; x1 <= 50; x1++)
        if (x1)
            for (int x2 = -50; x2 <= 50; x2++)
                if (x2)
                    for (int x3 = -50; x3 <= 50; x3++)
                        if (x3) ans += find(-(a1 * x1 * x1 *x1 + a2 * x2 * x2 * x2 + a3 * x3 * x3 * x3));

    printf("%d\n", ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值