多校第一场1010

求第n个数

题目描述:

Yellowstar likes integers so much that he listed all positive integers in ascending order,but he hates those numbers which can be written as a^b (a, b are positive integers,2<=b<=r),so he removed them all.Yellowstar calls the sequence that formed by the rest integers“Y sequence”.When r=3,The first few items of it are:
2,3,5,6,7,10……
Given positive integers n and r,you should output Y(n)(the n-th number of Y sequence.It is obvious that Y(1)=2 whatever r is).
n<=2*10^18,2<=r<=62,T<=30000.

题解:

首先正着想,给出一个n,求出它里面有几个合法的数. 先去掉2, 再去掉3,在加上6…就是这样容斥. 和莫比乌斯函数一样.提供符号. 然后容次来算, 注意. 之后二分合法的x,就是结果.
但是
这道题时间卡的很紧,有下面的优化;
(1)容次大于62的值直接不算.因为只有1,那么我们把一都不算,他们就都是0了,因此容次就大约是60的复杂度.(用莫比乌斯打表也一样)
(2)每次算不二分,而是用迭代. 最终结果x-f(x)=n, 我们写x=n+f(x).先把n当x算n+f(n),这个值一定小于等于x,那么就n+f(ans) 这个值也一定小于等于x,并且一直都是小于等于,并且一直再增加. 于是几次迭代就得到了.

重点:

发现容次直接可以正着算有几个.
容次的优化,去掉1之后很多0.
二分和迭代的学习

代码:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include <cmath>
#include <ctype.h>
#include <limits.h>
#include <cstdlib>
#include <algorithm>
#include <vector>
#include <queue>
#include <map>
#include <stack>
#include <set>
#include <bitset>
#define CLR(a) memset(a, 0, sizeof(a))
#define REP(i, a, b) for(ll i = a;i < b;i++)
#define REP_D(i, a, b) for(ll i = a;i <= b;i++)

typedef long long ll;

using namespace std;

const ll maxn = 1e9;
ll p[1000];
ll vis[1000];
ll pn;

void getP()
{
    CLR(vis);
    pn = 0;
    for(ll i = 2;i <= 100;i++)
    {
        if(vis[i]==0)
        {
            p[pn] = i;
            //printf("%d  %d\n", pn, p[pn]);
            pn++;
            for(ll j = i+i;j<=100;j+= i)
            {
                vis[j] = 1;
            }
        }
    }
}
ll key = 62;

ll ans;
ll r, n;
ll getF(ll x, ll res)
{
    if(res==1)
        return 0;
    ll t;
    t = (pow(x, 1.0/(res*1.0))+1e-9);
    return t - 1;
}
void calcu(ll x, ll pos, ll res, ll fu)
{
    if(res >= key)
        return;
    if(p[pos] > r)
    {
        ans += fu*(getF(x, res));
        return;
    }
    calcu(x, pos+1, res*p[pos], -fu);
    calcu(x, pos+1, res, fu);
}

ll f_old(ll n)
{
    ll l = 1, r = 2*n;
    while(r - 1 >= l)
    {
        ll mid = (l+r)/2;
        ans = 0;
        calcu(mid, 0, 1, 1);
        if(ans >= n)
        {
            r = mid;
        }
        else
        {
            l = mid + 1;
        }
    }
    return r;
}
ll getCha(ll tn)
{
    ans = 0;
    calcu(tn, 0, 1, -1);
    return ans + 1;
}
ll f(ll n)
{
    ll out = n + getCha(n);
    ll t;
    while((t = n+getCha(out)) != out)
    {
        //printf("%I64d---%I64d\n", getCha(out), getCha(n));
        out = n + getCha(t);
    }
    return out;
}

void solve()
{
    printf("%I64d\n", f(n));
}

int main()
{
  //  freopen("10Jin.txt", "r", stdin);
   // freopen("10Jout.txt", "w", stdout);
    getP();
    int t;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%I64d%I64d", &n, &r);
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值