ZOJ Problem Set - 4085

博客围绕ZOJ Problem Set - 4085题目展开,介绍补做很久以前比赛题的情况。阐述题意为给定函数Q(n,k)=m,已知k和m求最小的n。给出解题思路,分情况讨论,还给出构造解的方法,最后提及有对应代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ZOJ Problem Set - 4085

补的很久以前一场比赛的题

题意:

设一个函数QQQ: Q(n,k)=mQ(n,k)=mQ(n,k)=m

​ 代表将111~nnn所有数字按字典序排列,数字kkk的排列的位置是mmm

现在给你一个k​k​km​m​m,让你求最小的n​n​n

思路:

  • 如果kkk101010iii次方;那么下列讨论

    • 如果i+1=mi+1=mi+1=m 则答案为kkk
    • 否则 无解
  • 先求出111~kkk之间的字典序小于k的个数;可以for循环求出来

    • 如果个数tot>m−1tot>m-1tot>m1 则无解

    • 那么肯定有解,下面构造解

      • k′=kk'=kk=k
      • 每次在k′k'k后面加一个000,假设tot为这次在k′k'k后面添加000之前字典序小于kkk的个数。再次计算111~k′k'k小于kkk的个数
      • 如果个数>=m−1>=m-1>=m1,则解在$k’ $之前,且解为10len−1+m−1−tot−110^{len-1}+m-1-tot-110len1+m1tot1len为k′的位数len为k' 的位数lenk)
      • 否则令tottottot为此时111~k′k'k小于kkk的个数。返回第二步

代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6;
ll k,m,n;
ll Pow10[20];
ll arr[20];
void init()
{
    Pow10[0]=1,Pow10[1]=10;
    for(int i=2;i<19;++i)   Pow10[i]=Pow10[i-1]*10ll;
}
/*
计算1~k中比k小的数有多少个
排列组合的加法原理   一位数,二位数,三位数...len位数字 字典序小于k的个数之和
随后一直在k后面加0 计算比k小的数有多少个
*/
void calc()
{
    ll mid=k;
    int top=0;
    do{
        arr[++top]=mid%10;
        mid/=10;
    }while(mid);
    ll Pretot=0;
    ll Presum=0,ans=0;
    /*特判10的次方*/
    for(int i=0;i<=8;++i)
    {
        if(Pow10[i]==k)
        {
            if(m==i+1)
                printf("%lld\n",k);
            else
                puts("0");
            return ;
        }
    }
    for(int i=top;i;--i)
    {
        Presum=Presum*10+arr[i];
        Pretot+=Presum-Pow10[top-i];
        if(i!=1)
            Pretot++;
    }
    if(Pretot>m-1)
        puts("0");
    else if(Pretot==m-1)
        printf("%lld\n",k);
    else{
        /*不停的往后面添加0,记录小于k的个数 并比较答案*/
        for(;;)
        {
            top++;
            Presum*=10ll;
             if(Pretot+Presum-Pow10[top-1]>=m-1)
             {
                 ans=Pow10[top-1]+m-1-Pretot-1;//还需添加m-1-ans个在Pow10(top-1)后面  因为有最后一个属包括0,所以把再次-1 表示把0算上
                 printf("%lld\n",ans);
                 return ;
                    /*找出答案*/
             }
             Pretot+=Presum-Pow10[top-1];
        }
    }
    return ;
}
int main()
{
    int t;
    init();
    scanf("%d",&t);
    while(t--)
    {
        scanf("%lld %lld",&k,&m);
        calc();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值