#COCI11-12 #4 #纠结的数(二分+枚举+容斥)

31 篇文章 0 订阅
14 篇文章 0 订阅

1986: 【COCI11-12 #4】纠结的数

时间限制: 1 Sec  内存限制: 128 MB

题目描述

    找出第N小的正整数X,满足条件X的最小的素因子是P。如果X的值超过10^9,则输出0。

输入

第1行:2个整数N和P (1 ≤ N, P ≤ 10^9),P一定是素数

输出

1行:满足条件的数X或者0

样例输入

2 3

样例输出

9

这道题对p进行讨论,当p>=50时枚举打标记可过,

当p < 50时,二分rt,Ans = rt * p,

容斥去求能被p整除但不能被小于p的素数整除的数的个数,看是不是第N个

这题的容斥用循环,(1 << cnt)的二进制数位表示各个素数是否选中的情况,然后根据当前的交集个数来确定正负号

想了想还是要给出两个版本,一个 for 的容斥和一个 Dfs 版的容斥


Code:

For版(这是笔者的):

#include<iostream>
#include<cstdio>
using namespace std;

typedef long long LL;

const int MAX = 1000000000;

int N, P, Ans, cnt;
int prime[10005];

void Get_Prime(){
	static bool flg[100005];
	for(int i = 2; i < P; ++ i){
		if(! flg[i])	prime[++ cnt] = i;
		for(int j = i * i; j <= P; j += i)
			flg[j] = 1;
	}
}

bool check(int x){
	int sign, pro, tot = x;
	for(int i = 1; i < (1 << cnt); ++ i){
		sign = pro= 1;
		for(int j = 1; j <= cnt; ++ j)
			if(i & (1 << (j - 1))){
				pro *= prime[j];
				if(pro > x)	break;
				sign *= -1;
			}
		tot += x / pro * sign;
	}
	return tot >= N;
}

void Work(){
	if(N == 1){
		Ans = P;
		return ;
	}
	if(P * P < 0 || P * P > MAX){
		Ans = 0;
		return ;
	}
	Get_Prime();
	if(P < 50){
		int l = 1, r = MAX / P, mid, rt = 0;
		while(l <= r){
			mid = (l + r) >> 1;
			if(check(mid))
				rt = mid, r = mid - 1;
			else l = mid + 1;
		}
		if(! rt)	Ans = 0;
		else Ans = rt * P;
	}
	else {
		static bool Flg[MAX / 50 + 5];
		int tot = 0, mx = MAX / P;
		for(int i = 1; i <= cnt; ++ i)
			for(int j = prime[i]; j <= mx; j += prime[i])
				Flg[j] = 1;
		for(int i = 1; i <= mx; ++ i)	if(! Flg[i]){
			++ tot;
			if(tot == N){
				Ans = P * i;
				return ;
			}
		}
	}
}

int main(){
	scanf("%d%d", &N, &P);
	Work();
	printf("%d\n", Ans);
	return 0;
}


Dfs版(这是笔者损友的,速度更优):

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
   
const int Max=1e9;
using namespace std;
int n,p;
   
int gcd(int a,int b){
    return !b?a:gcd(b,a%b);
}
int lcm(int a,int b){
    return a*b/gcd(a,b);
}
int prime[10005],cnt;
 
void sieve(int P){
    static bool flag[100005]; 
    for(int i = 2; i < P; ++ i){
        if(! flag[i])   prime[++ cnt] = i;
        for(int j = i * i; j <= P; j += i)
            flag[j] = 1;
    }
}
  
int a,b;
void dfs(int i,int data,int flag,int &ans){
    if(i>cnt)
    {
        if(flag&1) ans+=b/data;
        else ans-=b/data;
    }
    else
    {
        dfs(i+1,lcm(data,prime[i]),flag+1,ans);
        dfs(i+1,data,flag,ans);
    }
}
bool judge(int x){
    b=x*p;
    int ans=0;
    dfs(1,p,1,ans);
    return ans>=n;
}
int work(){
    if(n==1) return p;
    if(p * p < 0 || p * p > Max)
        return 0;
    sieve(p);
    if(p<50)
    {
        int l=1,r=Max/p,mid,rt=0;
        while(l<=r)
        {
            mid=(l+r)>>1;
            if(judge(mid)) rt=mid,r=mid-1;
            else l=mid+1;
        }
        if(rt)
            return p*rt;
        else return 0;
    }
    else
    {
     
    static bool vis[Max/50+5];
        int up=Max/p;
        for(int i=1;i<=cnt;i++)
            for(int j=prime[i];j<=up;j+=prime[i])
                vis[j]=1;
        int i, tot = 0;
        for(i=1;i<=up;i++)
        {
            if(!vis[i]) ++ tot;
            if(tot==n) return p*i;
        }
        return 0;
    }
}
int main()
{
    scanf("%d%d",&n,&p);
    printf("%d",work());
}





  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值