zoukankan      html  css  js  c++  java
  • 折半枚举+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 }
  • 相关阅读:
    jquery中选择块并改变属性值的方法
    Bash Shell中Shift用法分享
    linux批量备份服务器配置文件和目录的脚本
    [Java开发之路](8)输入流和输出流
    cf 251 B Playing with Permutations 暴力 分类讨论
    CSS改变字体下划线颜色
    DICOM:C-GET服务
    Android平台录音音量计的实现
    【BZOJ】2186 沙拉公主的困惑
    【php】global的使用与php的全局变量
  • 原文地址:https://www.cnblogs.com/romaLzhih/p/9489821.html
Copyright © 2011-2022 走看看