一个简单的问题

HDU 2601 An Easy Problem (HDU 1st "Vegetable-Birds Cup" Programming Opening Contest)

Time Limit:3000ms(C/C++) / 6000ms(Java) Memory Limit:32768KB

问题描述

当Teddy还是一个孩子的时候,他常常思考一些简单的数学问题。一天,Teddy在梦中遇见了一位叫“Rulai”的老人给出了这样的一个问题:

给定一个正整数$N$,你能计算出有多少种写出$N = ij + i + j$(其中$ i,j $是满足 $i ≤ j $的正整数)的方法吗?(注:换句话说也就是有多少组满足条件的正整数$i, j$)Teddy找到了$N$小于$10$的解,但是随着$N$增大,他就发现这个问题对自己来说太难了,以至于无法解决。

那么,作为聪明的ACMer,你能帮助Teddy解决这个问题,让他睡上一个好觉吗?

输入

第一行包含一个正整数 $T$,$T≤2000$,而后的T行内只有一个非负整数 $N$,$N<10^{10}$。

输出

对每一个测试样例,在一行内给出问题的解。

样例输入

2

1

3

样例输出

0

1

这道题要求给出满足条件的$i,j$的组合的个数。观察条件的式子$N=ij+i+j$,两边同时加上$1$,就可得到$N + 1 = ij + i + j + 1$,于是就有$N + 1 = (i + 1) (j + 1)$问题就可以转换为已知一个数$N′=N+1$,求其因子的问题了,即$N′ = pq$ , $p ≤ q$。求因子,注意循环条件,可以用sqrt函数先求平方根来缩小遍历的范围(其实是将$O(n)$降低到了$O(sqrt{n})$)。我的C++实现如下:

 1 #include<iostream>
 2 #include<cmath>
 3 int main()
 4 {
 5     using namespace std;
 6     long long int N;
 7     int n;
 8     int mid;
 9     int count;
10     long long int div;
11     scanf("%d", &n);
12     for (int k = 0; k < n; k++)
13     {
14         cin >> N;
15         mid = sqrt(N + 1);
16         count = 0;
17         for (long long int i = 2; i <= mid; i++)
18         {
19             div = (N + 1) / i;
20             if ((N + 1) % i == 0 && div >= i)
21                 count++;
22         }
23         printf("%d
", count);
24     }
25     return 0;
26 }

ps.这个问题要求$N ≤ 10^{10}$,32位整数是要出现溢出的,这个需要留心一下。另外这个题被卡了两次常数TLE,之前看到有传闻说是printf scanfC++ cin cout速度快,就使用了这两个C函数;另外在某些OJ上,64位整数用printf %lld %l64d兼容性并不好,Codeforces直接建议用cin,所以这里使用了cin。

另外这个题咋一看难度不是很大,似乎直接用两个循环穷举$i,j$($i$从$0$到$sqrt{N}$,$j$从$i+1$到$sqrt{N}$)就可以了。不过留心题目给的时间限制和数据取值范围的话,$O(n)$的复杂度(两个$O(sqrt{n})$相乘)可能还是有问题的。我后来试着用这种方法写了一下,在我的i7 6800K @ 4.0GHz下,测试单个最大数据$10^{10}$时,已经使用了不下5秒,那么对于$T≈2000$的规模,TLE是必然的了。

 

原文地址:https://www.cnblogs.com/ggggg63/p/6417152.html