HDU 3641 Treasure Hunting

这道题貌似是10年杭州网络赛的题,乍一看挺唬人的,数据范围那么大。实际上就是个简单数论,看完题就想到解法了,只不过细节上要注意很多。

下面是详细解法:

a1^b1*a2^b2*a3^b3…*an^bn ,对于这个序列,我们把每个a都质因子分解,然后整个序列中质因子的种类和个数就都知道了,然后就要求X了,对于某个X的阶乘中含有的某个质因子的个数,这个有个很简单的结论,也很好理解,log(n)时间内就能得出结果,不知道的可以去面壁了。

for(k = factor; k <= x; k *= factor)

sum+= x/k;

大概就是这样子,其中factor是质因子,sum就是x!的该因子的个数了。

然后呢,就是二分答案了。

二分上限也是很有讲究的,可以自己根据输入的数据求出个上限,不过对于所有数据来说,因为100以内最大的质数貌似是97吧,极限数据应该是全是97,b都是最大,那么只要满足这种情况,所有情况的上限都可以在范围内了,我就先写了个程序跑出了上限,而且我发现,直接开到2^63-1之类的数是不行的,中间会出现溢出。

二分过程中,就判断x!的所有因子数是不是都能满足给出的序列中所要求的因子数。满足的话,肯定能整除了,然后逐渐缩小范围。

#include <iostream> #include <vector> #include <list> #include <map> #include <set> #include <deque> #include <queue> #include <stack> #include <bitset> #include <algorithm> #include <functional> #include <numeric> #include <utility> #include <sstream> #include <iomanip> #include <cstdio> #include <cmath> #include <cstdlib> #include <cctype> #include <string> #include <cstring> #include <cmath> #include <ctime> #define MAXN 205 #define INF 100000000 #define eps 1e-9 #define L(x) x<<1 #define R(x) x<<1|1 using namespace std; bool tag[101]; __int64 p[101]; int cnt = 0; void get_prime() //线性筛素数 { cnt = 0; for (__int64 i = 2; i < 101; i++) { if (!tag[i]) p[cnt++] = i; for (__int64 j = 0; j < cnt && p[j] * i < 101; j++) { tag[i*p[j]] = 1; if (i % p[j] == 0) break; } } } __int64 a[105]; __int64 b[105]; __int64 num[105]; __int64 tmp[105]; int main() { //freopen("d:/data.in","r",stdin); //freopen("d:/data.out","w",stdout); __int64 sum = 0; __int64 i, j; int t, n; scanf("%d", &t); get_prime(); while(t--) { memset(num, 0, sizeof(num)); __int64 high = 96000000099999999LL; //二分上限 __int64 low = 0LL; scanf("%d", &n); for(i = 0; i < n; i++) { scanf("%I64d%I64d", &a[i], &b[i]); for(j = 0; j < cnt && p[j] * p[j] <= a[i]; j++) //对a[i]因子分解 { __int64 ct = 0; if(a[i] % p[j] == 0) { while(a[i] % p[j] == 0) { ct++; a[i] /= p[j]; } } num[p[j]] += ct * b[i]; } if(a[i] > 1) { num[a[i]] += b[i]; } } __int64 ans = 0; while(low <= high) { __int64 mid = (low + high) / 2; memset(tmp, 0, sizeof(tmp)); for(i = 0; i < cnt && p[i] < 100; i++) { for(j = p[i]; j <= mid; j *= p[i]) { tmp[p[i]] += mid / j; } } int big = 0; for(i = 0; p[i] < 100; i++) { if(num[p[i]] == 0) continue; if(num[p[i]] > tmp[p[i]]) { big = -1; break; } } int cn = 0; for(i = 0; p[i] < 100; i++) if(num[p[i]] > 0) cn++; int nc = 0; for(i = 0; p[i] < 100; i++) { if(num[p[i]] == 0) continue; if(tmp[p[i]] >= num[p[i]]) nc++; } if(nc == cn) big = 1; if(big == 0) break; else if(big == -1) { low = mid + 1; } else if(big == 1) { high = mid - 1; } } printf("%I64d\n", low); } return 0; }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值