Codeforces 912 E.Prime Gift (折半枚举、二分)

题目链接:Prime Gift

题意:

  给出了n(1<=n<=16)个互不相同的质数pi(2<=pi<=100),现在要求第k大个约数全在所给质数集的数。(保证这个数不超过1e18)

题解:

  如果暴力dfs的话肯定超时间,其实给的n数据范围最大是16是一个很奇妙的数(一般折半枚举基本上是这样的数据范围@。@~)。所以想到折半枚举,把所有的质数分成两份求出每份中所有小于1e18的满足条件的数。然后二分答案,写cheak函数时遍历第一个集合,对第二个集合二分(折半枚举基本上这个套路)。但是,这里一定要注意的是这里折半枚举指的并不是将n分成两份求出两个集合,而是要让分出的两份数所形成的集合中所含的个数接近,因为用较小的数形成的集合个数要多很多(被这个点卡了好久#。#)。

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 const int MAX_N = 1e4+9;
 4 const long long INF = 1e18;
 5 long long N,M,T,k;
 6 long long vec[2][MAX_N];
 7 vector<long long> st[2];
 8 void dfs(int l,int r,long long rt,int id)
 9 {
10     st[id].push_back(rt);
11     for(int i=l;i<r;i++)
12     {
13         if(INF/vec[id][i] >= rt)
14             dfs(i,r,rt*vec[id][i],id);
15     }
16 }
17 long long cheak_num(long long x)
18 {
19     long long ans = 0;
20     for(int i=0;i<st[0].size();i++)
21     {
22         if(st[0][i] )
23         ans += upper_bound(st[1].begin(),st[1].end(),x/st[0][i]) - st[1].begin();
24     }
25     return ans;
26 }
27 int main()
28 {
29     while(cin>>N)
30     {
31         st[0].clear();st[1].clear();
32         for(int i=0;i<min((long long)5,N);i++) scanf("%lld",&vec[0][i]);
33         for(int i=0;i<N-min((long long)5,N);i++) scanf("%lld",&vec[1][i]);
34         dfs(0,min((long long)5,N),1,0);
35         dfs(0,N-min((long long)5,N),1,1);
36         for(int i=0;i<2;i++)
37             sort(st[i].begin(),st[i].end());
38
39         cin>>k;
40         long long l=1,r=INF;
41         while(l<=r)
42         {
43             long long mid = (l+r)>>1;
44             if(cheak_num(mid) < k) l = mid+1;
45             else r = mid-1;
46         }
47         cout<<l<<endl;
48     }
49     return 0;
50 }
51 /*
52 16
53 2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53
54 755104793
55 */

原文地址:https://www.cnblogs.com/doggod/p/8414075.html

时间: 2024-10-08 21:05:19

Codeforces 912 E.Prime Gift (折半枚举、二分)的相关文章

CSU OJ PID=1514: Packs 超大背包问题,折半枚举+二分查找。

1514: Packs Time Limit: 10 Sec  Memory Limit: 128 MBSubmit: 61  Solved: 4[Submit][Status][Web Board] Description Give you n packs, each of it has a value v and a weight w. Now you should find some packs, and the total of these value is max, total of

Eqs 折半枚举+二分查找 大水题

Eqs 题目抽象:a1x13+ a2x23+ a3x33+ a4x43+ a5x53=0 (*),给出a1,a2,a3,a4,a5.    ai属于[-50,50]. 求有多少序列   x1,x2,x3,x4,x5 ,xi属于 [-50,50]-{0}. 思路:折半枚举+二分查找 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #inclu

poj 2549 折半枚举+二分

三重循环肯定TLE,所以采用“折半枚举”的方法+二分查找来提高速度,不同的是需要保存两个下标用来判定是否有重复元素. 1 #include <algorithm> 2 #include <iostream> 3 #include <cstring> 4 #include <cstdio> 5 using namespace std; 6 7 const int N = 1000; 8 int a[N]; 9 int n, cnt; 10 11 struct

Codeforces 734C Anton and Making Potions(枚举+二分)

题目链接:http://codeforces.com/problemset/problem/734/C 题目大意:要制作n个药,初始制作一个药的时间为x,魔力值为s,有两类咒语,第一类周瑜有m种,每种咒语使制作一个药的时间变成a[i],花费b[i]的魔力,第二类咒语有k种,每种咒语瞬间产生c[i]个药,花费d[i]的魔力,c[i]和d[i]都是不递减的,求最短时间内产生n个药的时间.解题思路:因为c[i]和d[i]都是不降的,所以可以枚举a[i],然后二分查找花费小于t-b[i]的第二类咒语.注

poj3977(折半枚举+二分查找)

题目链接:https://vjudge.net/problem/POJ-3977 题意:给一个大小<=35的集合,找一个非空子集合,使得子集合元素和的绝对值最小,如果有多个这样的集合,找元素个数最少的. 思路:显然,可以用折半搜索,分别枚举一半,最大是2的18次方,复杂度能够满足.因为集合非空,枚举时考虑只在前一半选和只在后一半选的情况.对于前一半后一半都选的情况,把前一半的结果存下来,排序,枚举后一半的时候在前一半里二分查找最合适的即可. 思路不难,实现有很多细节,最开始用dfs写得一直wa,

Subset---poj3977(折半枚举+二分查找)

题目链接:http://poj.org/problem?id=3977 给你n个数,找到一个子集,使得这个子集的和的绝对值是最小的,如果有多种情况,输出子集个数最少的: n<=35,|a[i]|<=10e15 子集个数共有2^n个,所以不能全部枚举,但是可以分为两部分枚举: 枚举一半的所有情况,然后后一半二分即可: #include<iostream> #include<algorithm> #include<string.h> #include<st

912E - Prime Gift

912E - Prime Gift 思路: 折半枚举+二分check 将素数分成两个集合(最好按奇偶位置来,保证两集合个数相近),这样每个集合枚举出来的小于1e18的积个数小于1e6. 然后二分答案,check时枚举其中一个集合,然后找到另外一个集合小于mid/该元素的元素有多少个,这里用到一个双指针的技巧将复杂度降到O(n):一个集合从大到小枚举,那么mid/该元素就会逐渐变大,那么直接从上次找到的位置往后找就可以了,因为答案肯定在后面. 代码: #include<bits/stdc++.h>

Codeforces 912E Prime Gift(预处理 + 双指针 + 二分答案)

题目链接 Prime Gift 题意  给定一个素数集合,求第k小的数,满足这个数的所有质因子集合为给定的集合的子集. 保证答案不超过$10^{18}$ 考虑二分答案. 根据折半的思想,首先我们把这个集合的数分成两组. 然后分别生成这两组质数所能表示出的正整数的集合. 然后把这个集合sort一下,我们得到了两个有序的数列. 在计算小于等于某个数$x$的符合题目条件的数的时候,我们枚举第一个集合中的数, 用双指针定位和当前枚举到的数乘积恰好小于等于$x$的位置. 然后累加. 这里有一个细节,我们要

Educational Codeforces Round 76 F 折半枚举

Educational Codeforces Round 76 F 折半枚举 https://codeforces.com/contest/1257/problem/F 题意: 数组a,找到一个x使得a中每一个元素异或x后"二进制中1的个数"相同. 数组长度100,数字大小2^30. 思路: 折半枚举答案X,如分为X前15位和后15位.之后我们再枚举期望的"相同个数"是多少,通过hash看看能不能满足就好了. 代码: #include <bits/stdc++