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

题目连接 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;
}
全部评论

相关推荐

点赞 收藏 评论
分享
牛客网
牛客企业服务