Codeforces Round #456 (Div. 2) 912E E. Prime Gift

  OvO http://codeforces.com/contest/912/problem/E

  首先把这16个数字拆成2个子集,各自生成所有大小1e18及以下的积

  对于最坏情况,即如下数据

16
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53

  把她肢解成

  2 3 5 7 11 13 和  17 19 23 29 31 37 41 43 47 53 两个集合

  这两个集合生成的1e18及以下的积的数量分别为 958460个505756个,并不大,而且两个集合中的积必定是两两不相等的(除了1)。

  记两个集合大小的和为 |S| 

  两个集合生成的积各自排一下序

  然后二分答案,对于每个答案 u,可以 O(|S|) 得到他是第几大。

  具体做法是枚举从到小枚举第一个集合的积 t1,然后计算一下第二个集合的积中有多少积和 t1 相乘小于等于 u

  由于是从大到小枚举的,所以 t1 必然递增,所以第二个集合的积中符合条件的积的数量也必然是递增的,所以只要扫一遍就行。

  然后要注意的是直接用 long long 来进行计算比较好,double的精度貌似不太够

  (参考自这里->http://codeforces.com/contest/912/submission/33938779

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#include <vector>
#include <queue>
#include <vector>

using namespace std;

typedef long long ll;

const ll INF=1e18;
const int N=24;

vector<ll> seg[2];
int p[N],n;
ll ansid;

void dfs(int li,int ri,ll val,int id)
{
	seg[id].push_back(val);
	for(int i=li;i<=ri;i++)
		if(INF/p[i]>=val)
			dfs(i,ri,val*p[i],id);
}

ll cnt(ll num)
{
	int j=0;
	ll ret=0;
	for(int i=seg[0].size()-1;i>=0;i--)
	{
		while(j<seg[1].size() && seg[1][j]<=num/seg[0][i])
			j++;
		ret+=j;
	}
	return ret;
}

void solve()
{
	int i,j;
	dfs(1,min(6,n),1,0);
	dfs(min(6,n)+1,n,1,1);
	sort(seg[0].begin(),seg[0].end());
	sort(seg[1].begin(),seg[1].end());
//	cout<<seg[0].size()<<' '<<seg[1].size()<<endl;
	ll li=0,ri=INF,mid;
	while(li<ri-1)
	{
		mid=(li+ri)>>1;
		if(cnt(mid)>=ansid)
			ri=mid;
		else li=mid;
	}
	printf("%I64d
",ri);
}

int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&p[i]);
	scanf("%I64d",&ansid);
	solve();
	return 0;
}

/*

16
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53
2

*/

  

原文地址:https://www.cnblogs.com/FxxL/p/8213922.html