折半枚举+Hash(HDU1496升级版)

题目链接:N - 方程的解

给定一个四元二次方程:
$Ax1^2+Bx2^2+Cx3^2+Dx4^2=0$ 
试求$−1000≤x1,x2,x3,x4≤1000$非零整数解的个数。
$−10000≤A,B,C,D≤10000$

输出解的个数。

解法:
首先这道题直接用网上HDU1496的板子过不去,原因是1e10的数组开不了那么大的。所以这里只能换思路。新思路如下(很典型的折半枚举,也就是meet-in-middle):

  1. 把X1,X2的答案存下来(存在一个2000*2000的数组里面),然后排序
  2. 二分查找这个数组里面有多个数x了
  3. $AX_1^2+BX_2^2=-(CX_3^2+DX_4^2)$
  4. 左边式子的答案我们已经存下来了,接下来算右边的
  5. 右边答案算出来过后,我们直接在左边数组里面二分有多少个一样的数值,答案加上这个数值就ok了

当然这里用数组会稍显笨拙,可以用map,卡时间可以过。
注意s[a*i*i + b*j*j]++会爆int,因此需要将a改为long long

代码如下:

 1 #include<cstdio>
 2 #include<map>
 3 #include<algorithm>
 4 using namespace std;
 5 typedef long long ll;
 6 map<ll, ll>s;
 7 
 8 int main() {
 9     ll a, b, c, d;
10     while (scanf("%lld %lld %lld %lld", &a, &b, &c, &d) != EOF) {
11         if (a * b > 0 && b * c > 0 && c * d > 0) {
12             printf("0
");
13             return 0;
14         }
15         for (ll i = 1; i <= 1000; i++) {
16             for (ll j = 1; j <= 1000; j++) {
17                 //if (i == 0 || j == 0)continue;
18                 s[a*i*i + b*j*j]++;
19             }
20         }
21         ll sum = 0;
22         for (ll i = 1; i <= 1000; i++) {
23             for (ll j = 1; j <= 1000; j++) {
24                 //if (i == 0 || j == 0)continue;
25                 ll t = c*i*i + d*j*j;
26                 sum += s[0 - t];
27             }
28         }
29         printf("%lld
", 16*sum);
30     }
31     return 0;
32 }

上面说了卡常数不是很严的做法,如果卡常数很严的话,比如x的范围变到4000,map就会T掉,这里直接采用hash的方法

关键词:数字hash

例题:Uva1152:4 Values whose Sum is 0

 1 //hash数字编码
 2 #include<cstdio>
 3 #include<vector>
 4 #include<algorithm>
 5 #include<cstring>
 6 #include<map>
 7 using namespace std;
 8 typedef long long ll;
 9 map<ll, ll>s;
10 int a[4005], b[4005], c[4005], d[4005];
11 int n, T, cnt;
12 
13 //w[i]表示第i个结点存储的数(也就是a+b),st[i]表示第i个结点有多少种表示方法
14 const int hashsize = 1000003;
15 int hd[hashsize], nxt[16000005], w[16000005], st[16000005]; 
16 void in(int x) {
17     int h = (x % hashsize + hashsize) % hashsize, u = hd[h];
18     while (u) {
19         if (w[u] == x) {
20             st[u]++;
21             return;
22         }
23         u = nxt[u];
24     }
25     nxt[++cnt] = hd[h];
26     hd[h] = cnt;
27     w[cnt] = x;
28     st[cnt] = 1;
29 }
30 
31 int srch(int x) {
32     int h = (x % hashsize + hashsize) % hashsize;//查询的数是负数,所以要这么算;
33     int u = hd[h];
34     while (u) {
35         if (w[u] == x) return st[u];
36         u = nxt[u];
37     }
38     return 0;
39 }
40 
41 int main() {
42     scanf("%d", &T);
43     while (T--) {
44         cnt = 0; memset(hd, 0, sizeof(hd));
45         scanf("%d", &n);
46         int A, B, C, D;
47         for (int i = 0; i < n; i++) {
48             scanf("%d%d%d%d", &a[i], &b[i], &c[i], &d[i]);
49         }
50         for (ll i = 0; i < n; i++) {
51             for (ll j = 0; j < n; j++) {
52                 in(a[i] + b[j]);
53             }
54         }
55         ll sum = 0;
56         for (int i = 0; i < n; i++) {
57             for (int j = 0; j < n; j++){
58                 sum += srch(-c[i] - d[j]);
59             } 
60         }
61         printf("%lld
", sum);
62         if (T) printf("
");
63     }
64     return 0;
65 }
原文地址:https://www.cnblogs.com/romaLzhih/p/9489821.html