您的位置:首页 > 其它

JZOJ.1241. Number

2016-07-12 16:22 381 查看

Problem

Description

有N(2<=N<=15)个数A1,A2,….,An-1,An,如果在这N个数中,有且仅有一个数能整除m,那么整数m就是一个幸运数,你的任务就是在给定A1,A2,….,An-1,An的情况下,求出第k小的幸运数。

Input

第一行为一整数数N,K(2<=N<=15,1<=K<=2^31-1),意义如上述。

接下来一行有N个整数,A1,A2,….,An-1,An,这N个整数均不超过2^31-1。

Output

输出一行,仅包含一个整数ans,表示第K小的幸运数。答案保证不超过10^15。

Sample Input

输入1:

2 4

2 3

输入2:

2 100

125 32767

Sample Output

输出1:

8

输出2:

12500

Hint

对于50%的数据,N<=5,ANS<=100000

对于80%的数据,N<=10,ANS<=10^15

对于100%的数据,N<=15,ANS<=10^15

Solution

正解:二分+容斥

二分答案mid,现在就是要求在[1,mid]这个范围内,幸运数的个数Si。

则总个数为Σni=1mida[i]

如果它小于k则mid要增大,否则减小。

但是,这里面一定多算了很多的数。比如说序列{2,3}其中6不是幸运数,2这里算了一次,3这里又算了一次,所以个数要-2.(6是2,3的交集,所以我们要减去2和3的交集)

由容斥原理得,对于一个集合,如果集合元素个数为基数,则加上这个集合所有元素的LCM*集合元素个数否则减去。

由于N<=15,我们可以用2n 的时间来查找所有的集合。

总时间复杂度为O(log(1015)∗2n∗算LCM的时间).

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define LL long long
using namespace std;
LL a[20],l,mid,r,ans,sum;
int i,n,k;
LL gcd(LL a,LL b)
{
if (b==0) return a;else return gcd(b,a%b);
}
LL lcm(LL a,LL b)
{
if (a>b) swap(a,b);
return a*b/gcd(a,b);
}
void select(LL x,LL s,LL cnt)
{
if (x>n)
{
if (cnt>0)
{
if (cnt%2==1) ans+=(mid/s)*cnt;
else ans-=(mid/s)*cnt;
}
return;
}
LL w;
select(x+1,s,cnt);
w=lcm(s,a[x]);
if (w>mid) return;   //特别注意:如果LCM超过了mid要退出去,否则会爆炸。
select(x+1,w,cnt+1);
}
int main()
{
scanf("%d%d",&n,&k);
fo(i,1,n) scanf("%lld",&a[i]);
l=1;
r=1000000000000000;
while (l<r)
{
mid=(l+r)/2;
ans=0;
select(1,1,0);
if (ans<k) l=mid+1;else r=mid;
}
printf("%lld",l);
}


——2016.7.12
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: