“字节跳动-文远知行杯”广东工业大学第十四届程序设计竞赛

题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=6463

Problem Description
通常来说,题面短的题目一般都比较难,所以我要把题面写得很长很长。
通常来说,题面短的题目一般都比较难,所以我要把题面写得很长很长。
通常来说,题面短的题目一般都比较难,所以我要把题面写得很长很长。
鸽子数字由以下过程定义:从任何正整数开始,将数字替换为其各个数位的平方和,并重复该过程,直到该数字等于1。如果不能,则这个数字不是鸽子数。
例如7是鸽子数,因为7->49->97->130->10->1。(77=49,44+99=97,99+7*7=130…如此类推)
显然1是第一个鸽子数。
有Q个询问,每个询问给出一个数k,你需要输出第k个鸽子数。

Input

第一行一个Q,代表询问的个数(Q<=100000)
接下来Q行,每行一个数字k(k<150000)

Output

每行输出一个数,代表第k个鸽子数

Sample Input

2
1
2
Sample Output
1
7

一做题步骤:

  1. 看懂题意
  2. 分析题目,找到隐藏解题条件和方向,注意细节
  3. 考虑题目使用的算法和解题的思路
  4. 时间复杂度和算法优化,以及思路优化
  5. 检查,数组大小和初始化等细节

1 题意:
从任何正整数开始,将数字替换为其各个数位的平方和,并重复该过程,直到该数字等于1。如果不能,则这个数字不是鸽子数。
2.分析问题:
鸽子数一看就明白,但题目要我们找的是第几个鸽子数。
分析1:这是一个规律题,可以考虑是否可以找到它规律。
找规律的方式是打印出格子数或者是鸽子数的一些规律。
分析2:技巧优化,暴力枚举
3 题目使用的算法和解题的思路
显然根据题目,不会是某一个已经学习的算法,这个是一个需要找出解题思路的题目;
解法思路1
对于鸽子数,你可以打印每一次循环,是鸽子数和不是鸽子数的数的规律。
你会发现不是鸽子数的时候,每个数运算到一定步骤后都会算到一个数,然后都会走到同样的一步。所以这就是规律!!!!
不是鸽子数的数循环到89 就结束;是鸽子数就循环到1结束。
这样就不会2无限循环了。

解法思路2:
对于这个题目,就是对于一个不是鸽子数的数,平方和后,一定会重复出现某个数,我们用数组记录这个出现的数,然后下次循环到这个数说明此路不同。
对于所有算过的数都标记一下,判断是鸽子数的记录下来,
4.时间复杂度和算法优化
O(1000001*10)左右
5. 检查,数组大小和初始化等细节
题目中的鸽子数的数目,大概有100001多,数组要开大一点;

二 比赛后总结和提高:

  1. 考察算法
  2. 思维提升
  3. 不同解题思路
  4. 以后做这类题目的时候,应该怎么做
  5. 需要提高什么?

1.考察算法

2.思维提升
这个题目,需要捉住一个重点,数字替换为其各个数位的平方和,并重复该过程,直到该数字等于1。这个过程一开始不知道会不会产生规律,如果你有灵感你会决定不是鸽子数的时候会有走到一个相同的方向,就是每一次位数的平方得到的数字都会走到同一个数。

想不出的时候,敲代码模拟出是鸽子数的规律和不是鸽子数的规律。

这种题目往往需要从逆方向思考,找不是鸽子数的规律。

找规律,我们不仅仅是一个数一个数的规律,也可能是运算过程中的规律。
3.不同解题思路
直接找出运算规律,不找确定出某个值,将运算规律运用。
4.以后做这种题目
先逆方向去想这个问题。
每想法,就代码模拟。
5.需要提高
逻辑能力和逆向思考的能力。

#include<stdio.h>
#include<iostream>
#include<string.h>
using namespace std;
#define ll long long
ll num[150010];
int cnt;
int main()
{
   
    for(ll i=1;;i++)
    {
   
        ll mm=i;
        while(mm!=1&&mm!=89)//规律
        {
   
            ll d=mm;
            mm=0;
            while(d!=0)
            {
   
                mm+=(d%10)*(d%10);
                d/=10;
            }
        }
        if(mm==1)
        {
   
            num[cnt++]=i;
            if(cnt==150000)
                break;
        }
    }
    int q,k;
    scanf("%d",&q);
    while(q--)
    {
   
        scanf("%d",&k);
        printf("%lld\n",num[k-1]);
    }
    return 0;
}

链接:http://acm.hdu.edu.cn/showproblem.php?pid=6467
已知
F(n)=∑i=1n(i×∑j=inCij)
求 F(n) mod 1000000007
Input
多组输入,每组输入占一行,包含一个整数n(1 <= n <= 1e18)。
数据不超过300000组。

Output
对于每组输入,输出一行,包括一个数代表答案。
Sample Input
5
100
Sample Output
129
660756544

找出它 的递推式 (n-1)*2^n+1;
然后注意它的数据很大,一般算肯定超时,所以要优化,用快速幂就可以。
  附上求快速幂a^bmodc  
int ans = 1;
a = a % c;
while(b>0){
    if(b % 2 == 1)
    ans = (ans * a) % c;
    b = b/2;
    a = (a * a) % c;
}
printf("%d",ans);
#include<stdio.h>
#include<algorithm>
#include<string.h>
#include<iostream>
using namespace std;
#define ll long long
int main()
{
    ll n,m;
    ll mod=1000000007;
    while(~scanf("%lld",&n))
    {
        m=n;
       ll sum=1,a=2;
        while(n!=0)
        {
            if(n%2==1)
                sum=(sum*a)%mod;
            n=n/2;
            a=(a*a)%mod;
        }
        sum=(sum*(m%mod-1)%mod+1)%mod;
        printf("%lld\n",sum);
    }
}

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6468
zyb的面试
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 560 Accepted Submission(s): 202

Problem Description
今天zyb参加一场面试,面试官听说zyb是ACMer之后立马抛出了一道算法题给zyb:
有一个序列,是1到n的一种排列,排列的顺序是字典序小的在前,那么第k个数字是什么?
例如n=15,k=7, 排列顺序为1, 10, 11, 12, 13, 14, 15, 2, 3, 4, 5, 6, 7, 8, 9;那么第7个数字就是15.
那么,如果你处在zyb的场景下,你能解决这个问题吗?

Input
T组样例(T<=100)
两个整数n和k(1<=n<=1e6,1<=k<=n),n和k代表的含义如上文

Output
输出1-n之中字典序第k小的数字

Sample Input
1
15 7

Sample Output
15

Source
“字节跳动-文远知行杯”广东工业大学第十四届程序设计竞赛

Recommend
liuyiding | We have carefully selected several similar problems for you: 6470 6469 6468 6467 6466
DFS+暴力;
题意是按字典序排序,在n-个中找出第m个数是什么;
按题意,可以很明显就可以知道

              1                                                                                2
10 11 12 13 14 15 16 17 18 19                               20 21 22 23 24 25 26 27 28 29

100 101 102 103 104 105 106 107 108 109
1000 1001
10000 …
100000 …
解题思路:如果它们之间用树状图表示,我们找字典序的第m小的数也就是从1 开始,向左儿子下面搜索到最后再返回,还有一个地方是,当搜到s>=m时我们就循环return ;结束,否则继续递推搜索,直到找到答案;

#include<stdio.h>
#include<iostream>
using namespace std;
int cc,an,n,m;
void dfs(int x)
{
    if(cc>=m)
        return ;
    an=x;//从1 开始,
    cc++;//每一次搜索;
    for(int i=x*10;i<=x*10+9&&i<=n;i++)//每一增一层就是十倍,从左到右就是9 个,还要注意的一点是i<=n;不能超过所给的排序的数,
    {
        dfs(i);
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        an=0;
        cc=0;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=9&&i<=n;i++)//排序的数字很明显就只是1-9;
            dfs(i);
        printf("%d\n",an);
    }
    return 0;
}

另一种写法(先占坑,后再补);

#include<stdio.h>
#include<algorithm>
#include<iostream>
#define ll long long
using namespace std;
ll arr[150000];
ll Pow10(ll a)
{
    ll sum=1;
    for(int i=1;i<=a;i++)
    sum*=10;
    return sum;
}
//ll Pow10[i*10];
ll haa(ll val)
{
    ll tot=0;
    while(val)
    {
        tot++;
        val/=10;
    }
    return tot;
}
ll waha(ll val,ll n)
{
    ll Presum=0;
    ll Pretot=0;
    ll mid=val;
    ll top=0;
    do{
        arr[++top]=mid%10;
        mid/=10;
    }while(mid);
    for(ll i=top;i;--i)
    {
        Presum=Presum*10+arr[i];//
        Pretot+=Presum-Pow10(top-i);
        if(i!=0)
            Pretot++;
    }
    for(;;)
    {
        top++;
        Presum*=10;
        if(top<haa(n))
        {
            Pretot+=Presum-Pow10(top-1);
        }
        else if(top==haa(n))
        {
            Pretot+=min(Presum,n)-Pow10(top-1);//比较那个小,得出那个位数得排序数;
            if(Presum>n)
                Pretot++;
            return Pretot;
        }
        else
        return Pretot;
    }
}
int main()
{
    ll u,y;
    int t;
    while(t--){
    scanf("%lld%lld",&y,&u);
    printf("%lld\n",waha(u,y));
    }
    return 0;
}

Count
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 661 Accepted Submission(s): 258
Problem Description
Farmer John有n头奶牛.
某天奶牛想要数一数有多少头奶牛,以一种特殊的方式:
第一头奶牛为1号,第二头奶牛为2号,第三头奶牛之后,假如当前奶牛是第n头,那么他的编号就是2倍的第n-2头奶牛的编号加上第n-1头奶牛的编号再加上自己当前的n的三次方为自己的编号.
现在Farmer John想知道,第n头奶牛的编号是多少,估计答案会很大,你只要输出答案对于123456789取模.

Input
第一行输入一个T,表示有T组样例
接下来T行,每行有一个正整数n,表示有n头奶牛 (n>=3)
其中,T=104,n<=1018

Output
共T行,每行一个正整数表示所求的答案

Sample Input
5
3
6
9
12
15

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6470
Sample Output
31
700
7486
64651
527023

这道题比赛时超时,问了学长之后,学长交了我矩阵快速幂就AC了;
那现在我先来解决怎么堆出矩阵快速幂,首先就是要知道矩阵是什么,这个知识我就跳过。
我们有题意推出一个公式:F[n]=F[n-1]+2F[n-2]+nnn;
那么我们运用矩阵:
A =[F[n-1] F[n-2] n^3 n
n n 1]; 然后在建一个矩阵
B= [1 1 0 0 0 0]
2 0 0 0 0 0
1 0 1 0 0 0
0 0 3 1 0 0
0 0 3 2 1 0
0 0 1 1 1 0
AB,根据矩阵相乘可以得C=[F[n] F[n-1] (n+1)^3 (n+1)(n+1) (n+1) 2]
则,我们可以利用快速幂得模板进行 运算

#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const ll mod=123456789;
const int NN=6;
struct gg
{
    ll mm[NN][NN];
};
gg mmll(gg a,gg b)
{
    gg ans;
    for(int i=0;i<NN;i++)
    {
        for(int j=0;j<NN;j++)
        {
            ans.mm[i][j]=0;
            for(int k=0;k<NN;k++)
            {
                ans.mm[i][j]+=a.mm[i][k]*b.mm[k][j]%mod;
                ans.mm[i][j]%=mod;
            }
        }
    }
    return ans;
}
gg qiupow(gg a,ll b)
{
    gg ans;
    for(int i=0;i<NN;i++)
        for(int j=0;j<NN;j++)
            ans.mm[i][j]=0;
            for(int i=0;i<NN;i++)
                ans.mm[i][i]=1;
    while(b)
    {
        if(b&1)
        ans=mmll(ans,a);
        a=mmll(a,a);
        b/=2;
    }
    return ans;
}
int A[]={2,1,27,9,3,1};
int main()
{
    gg B;//矩阵B;
    B.mm[0][0]=1;B.mm[0][1]=1;B.mm[0][2]=0;B.mm[0][3]=0; B.mm[0][4]=0;B.mm[0][5]=0;
    B.mm[1][0]=2;B.mm[1][1]=0;B.mm[1][2]=0;B.mm[1][3]=0; B.mm[1][4]=0;B.mm[1][5]=0;
    B.mm[2][0]=1;B.mm[2][1]=0;B.mm[2][2]=1;B.mm[2][3]=0; B.mm[2][4]=0;B.mm[2][5]=0;
    B.mm[3][0]=0;B.mm[3][1]=0;B.mm[3][2]=3;B.mm[3][3]=1; B.mm[3][4]=0;B.mm[3][5]=0;
    B.mm[4][0]=0;B.mm[4][1]=0;B.mm[4][2]=3;B.mm[4][3]=2; B.mm[4][4]=1;B.mm[4][5]=0;
    B.mm[5][0]=0;B.mm[5][1]=0;B.mm[5][2]=1;B.mm[5][3]=1; B.mm[5][4]=1;B.mm[5][5]=1;
    ll t,n;
    scanf("%lld",&t);
    while(t--)
    {
        int a=0;
        scanf("%lld",&n);
        gg as=qiupow(B,n-2);//B的n-2次幂;
        if(n==1)
        {
            printf("1\n");
            continue;
        }
        if(n==2)
        {
            printf("2\n");
            continue;
        }
        for(int i=0;i<NN;i++)
        {
            a+=(A[i]*as.mm[i][0])%mod;
        }
        printf("%lld\n",a%mod);
    }
    return 0;
}
全部评论

相关推荐

不愿透露姓名的神秘牛友
2025-12-17 17:40
点赞 评论 收藏
分享
写下这篇文章的时候,我正坐在从学校飞往北京的飞机上。就在今天,我的秋招终于算是有了结论,一共60场面试,拿到了字节百度美团等10+大厂offers,最终确认了腾讯给的机会。同时给我的这三个月,这三年以及从今天往前的所有人生做了个结。这句话写的真好,为什么这么说呢?本来挺久之前我就想写点什么,有特别多想记录的,从选择这个专业到选择这个岗位,从科研的疲惫到未来生活的期待,但总感觉这样写没个纲,乱成一团。直到我今天正式在系统中点击了三方的确认,我才突然发现这种感觉就是“不可逃避的结束”在向我走来,于是纲便有了。首先是这三个月的结果吧,或者换句话说,其实是秋招的结果。从我硕士选择了强化学习的研究方向,我就知道并不会有太多的岗位。从试错中学习,这听起来很符合人类的学习方式,但实际场景中哪来那么多试错的成本?除了游戏产业和机器人行业,我想不到特别对口的赛道,而这两个行业国内又只有寡头,让我望而生畏。整个秋招,我没法像学后端开发的同学一样投递大量的简历,我没法像学大模型的同学一样是时代的香饽饽,我只能盯着那几家公司去投,或者想方设法的在别的不太相关的算法岗上沾沾边。方向是大于努力的,但努力一定不是不重要的。秋招整体对我来说还算顺利,前文就自然变成了只有我自己懂的无病呻吟,不再赘述。从结果来说,我的秋招是非常成功的,至少我自己是满意的。命运给了我很大的惊喜,我从未想过能够在这次有多个远超期待的offer,所以我如今是心满意足。虽说很多事都是焉知非福吧,但对口的工作内容,熟悉的工作环境,我一定不会后悔。我就是这样,毕竟让我在做一百次选择也不会变,那为什么要在不可预测的未来后悔。然后是三年,三年即将过去,我的硕士生涯来到了最后一章。回想过往,我在其中反复感受井底之蛙的狭隘。从我在二十多个四点睡的凌晨产出的论文初稿开始,链式反应就这样发生了。把论文投出去,我发了一篇很长的朋友圈,那时候觉得压力真的好大,尽管其实根本没人要求我什么。那时,我第一次觉得我比本科毕业时的自己进步了太多,可以独当一面了。然后去了北京自所交流,尽管大多的时间都在修改那篇返稿的文章,但也在不一样的平台中见识了人外有人的世界。回来后,我第二次觉得自己有了很大的进步,而鄙夷去北京前的自己是如此短浅。那是11月,我开始纠结到底未来该从事开发岗还是算法岗,但时间并没有给我机会。我偷懒了,两个月根本没有做任何开发岗的准备,于是只能硬闯算法。期间只有那篇论文中了让我稍微有些自信,毕竟只有两周的理论准备时间让我心里太虚了,这甚至还算上了刷题的时间。第一面就是最想去的公司,我甚至紧张到大脑一片空白。好在后面算是有惊无险,拿到了腾讯给我的实习机会。去腾讯工作的时间是幸福的,组里氛围也很好,在公司获得的提升我觉得甚至超过了我在学校一年的量。毕竟做算法,思维的敏捷度和见识广度都是如此重要。看着同事前辈们的工作能力,和工业级的项目架构,我又一次不由得感叹曾经自己的狭隘。于是每天我只睡五小时,忙完工作忙学校,每每想到这里,我也不觉得我的成功是侥幸了。我真的建议大家离开自己舒适的环境到外面看看,鸡头或许真的不如凤尾。硕士是一个连锁反应最直接,最有力的时期。高考失利或许还能补救,考研没上岸还有第二次机会,但就业前这一年,努力就是会有回报,就一定会体现在结果中,没有侥幸。最后,也是我最想聊的。十九年的学生生涯终于快要画下句号,我其实一直觉得非常梦幻。我能回忆起每一个瞬间,有小学六年级遇到的很有个性的数学老师,有考上重点中学的快乐,有中考和提前高考而大失败的难受,有本科比赛的每个通宵的焦虑,有保研出现差错的绝望,有刚读研高压之下的崩溃。但这篇长文不会再有更多的剧情了,每个故事都让我无限回味,成为了我一生中最宝贵的财富。这些瞬间组成了我。我父亲说我是一个总抓不住机会的人,确实有很多别人没有的机会摆在我面前,我都错过了。但我心中的热爱始终没有错过,我觉得这对我来说是幸运且幸福的。我非常爱打游戏,从初中开始学编程,第一个目的就是做出属于自己的游戏,做了很多小游戏发在班级群里,被人厌烦。高中自己买了unity的书,想做自己的游戏,无奈连网络的基本知识都不懂,无功而返。到了大学,我又被强化学习吸引,我想知道能不能让人工智能来帮我打游戏呢?这一整条线我没有放弃过,拿到了游戏算法offer,我真的特别特别开心。人不是一直成功的,我经历过的失败远超过成功10倍,但那让我知道成功来之不易,让我知道失败是生活常态,让我知道真正的怯懦不是不敢失败,而是不敢尝试。言尽于此,这些都“不可逃避的结束”了。追风赶月莫停留,平芜尽处是春山。
肖先生~:追风赶月莫停留,平芜尽处是春山,passion!
我的秋招日记
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务