Nefu大一ACM训练入门 个人题解「LYRICS」

Nefuer-lyrics 带来热乎的acm实验室训练入门 个人题解啦
如有错误 or 建议,欢迎评论区指正!

努力成为合格甚至优秀 A c m e r 的一天又一天 努力成为合格甚至优秀Acmer的一天又一天 努力成为合格甚至优秀Acmer的一天又一天

在这里插入图片描述

话不多说,进入正题咯!

A Rightmost Digit 最右边的数

题面大意:求 N N N^N NN 数位中最右边的数
微操一下即可发现对答案有贡献的仅是个位数和幂数 ( N ) (N) N
法一:分类讨论(理论依据:循环节)(更推荐法二)

如末尾为 1 1 1 的任何次方都是结果仍为 1 1 1
末尾为 2 2 2 1 / 2 / 3 / 4 / / 5 / 6 / 7 / 8 1/2/3/4//5/6/7/8 1/2/3/4//5/6/7/8 次方分别为 2 / 4 / 8 / 6 / / 2 / 4 / 8 / 6 2/4/8/6//2/4/8/6 2/4/8/6//2/4/8/6 ,有四个一循环
末尾为 3 3 3 的同理有 3 / 9 / 7 / 1 / / 3 … 3/9/7/1//3… 3/9/7/1//3的四个一循环
末尾 4 4 4 就是两种结果 4 / 6 4/6 4/6,末尾 5 / 6 5/6 5/6 就是本身……其他情况详见代码 e l i f elif elif 部分
注意数据存储时 x x x 为对应单循环节(如 末尾为2时 : 4 4 4 )的倍数时,数组索引 a [ 0 ] a[0] a[0] 应存原本意义下的 a [ 4 ] = 6 a[4]=6 a[4]=6

Code:
#include <bits/stdc++.h>

using namespace std;
#define IOS ios::sync_with_stdio(false), cin.tie(0);

int main()
{
    IOS
    int T;
    cin>>T;
    while(T--)
    {
        LL x;
        cin>>x;
        LL y=x%10;
        if(y==0) cout<<"0\n";
        else if(y==1) cout<<"1\n";
        else if(y==2)
        {
            LL t=x%4;
            int a[4]={6, 2, 4, 8};
            cout<<a[t]<<"\n";
        }
        else if(y==3)
        {
            LL t=x%4;
            int a[4]={1, 3, 9, 7};
            cout<<a[t]<<"\n";
        }
        else if(y==4)
        {
            LL t=x%2;
            if(!t) cout<<"6\n";
            else cout<<"4\n";
        }
        else if(y==5) cout<<"5\n";
        else if(y==6) cout<<"6\n";
        else if(y==7)
        {
            LL t=x%4;
            int a[4]={1, 7, 9, 3};
            cout<<a[t]<<"\n";
        }
        else if(y==8)
        {
            LL t=x%4;
            int a[4]={6, 8, 4, 2};
            cout<<a[t]<<"\n";
        }
        else if(y==9)
        {
            LL t=x%2;
            if(!t) cout<<"1\n";
            else cout<<"9\n";
        }
    }
    return 0;
}
法二 (强烈推荐!) :快速幂求 pow(n%10, n, mod=10)
#include <bits/stdc++.h>

using namespace std;
#define IOS ios::sync_with_stdio(false), cin.tie(0);

int qmi(int a, int k, int mod=10)
{
    int res=1;
    while(k)
    {
        if(k&1) res=res*a%mod;
        k>>=1;
        a=a*a%mod;
    }
    return res;
}
int main()
{
    IOS
    int T;
    cin>>T;
    while(T--)
    {
        int n;
        cin>>n;
        int a=n%10;
        cout<<qmi(a, n)<<"\n";//偷懒用的默认参数
    }
    return 0;
}

B 数的长度

题面大意:求 N ! N! N 在十进制下的数位长度
数字 n n n 在十进制下的数位长度为 l e n = ⌊ log ⁡ 10 n ⌋ + 1 len=\lfloor\log_{10}n\rfloor+1 len=log10n+1

a n s = ⌊ log ⁡ 10 N ! ⌋ + 1 = ⌊ log ⁡ 10 N + log ⁡ 10 ( N − 1 ) + … + log ⁡ 10 1 ⌋ + 1 ans=\lfloor\log_{10}N!\rfloor+1=\lfloor\log_{10}N+\log_{10}(N-1)+…+\log_{10}1\rfloor+1 ans=log10N!⌋+1=log10N+log10(N1)++log101+1
至此,做法已成

#include <bits/stdc++.h>

using namespace std;
#define IOS ios::sync_with_stdio(false), cin.tie(0);

int main()
{
    IOS
    int n;
    while(cin>>n)
    {
        double ans=0;
        for(int i=n; i>=1; i--)
            ans+=log10(i);
        cout<<(int)ans+1<<"\n";
    }
    return 0;
}

C 最左边的数

题面大意:求 N N N^N NN 数位中最左边的数

作为选拔赛的防AK题,难点就在于考虑到 log ⁡ 10 x \log_{10}x log10x
下面给出一个纯数学推导的证明:

Code
#include <bits/stdc++.h>

using namespace std;
#define IOS ios::sync_with_stdio(false), cin.tie(0);

int main()
{
    IOS
    int T;
    cin>>T;
    while(T--)
    {
        int x;
        cin>>x;
        double t=x*log10(x);
        double d=t-floor(t);
        cout<<(int)pow(10, d)<<"\n";
    }
    return 0;
}

D 数字序列

题面大意:

f ( 1 ) = 1 , f ( 2 ) = 1 , f ( n ) = A ∗ f ( n − 1 ) + B ∗ f ( n − 2 ) % 7 f(1)=1, f(2)=1, f(n)=A*f(n-1)+B*f(n-2) \%7 f(1)=1,f(2)=1,f(n)=Af(n1)+Bf(n2)%7
给定 A , B < = 1000 , n < = 1 e 8 A,B<=1000, n<=1e8 A,B<=1000,n<=1e8, 求 f ( n ) f(n) f(n)

注意到递推公式中是对7取模,直观上感受这个 f(n) 的取值区间这么小,在 n 很大时必然存在循环节,且单个循环节不会很长

我是用 m a p map map 存下来 公式中 f [ i − 1 ] f [ i − 2 ] f[i-1] f[i-2] f[i1]f[i2] 出现的索引 i − 1 i-1 i1,每次更新 f [ i ] f[i] f[i] 后在 m p mp mp 里找下有没有这对数出现过,如果有那就找到了最小循环节
// 代码中特别处理的 p o s = = 0 pos==0 pos==0 好像也可以不用哈

#include <bits/stdc++.h>

using namespace std;
#define IOS ios::sync_with_stdio(false), cin.tie(0);

int a, b, n;

int find()
{
    if(n==1||n==2) return 1;
    vi f(n+1);
    f[1]=f[2]=1;
    map<PII, int> mp;//f[i-1]f[i-2]映射到idx

    for(int i=3; i<=n; i++)
    {
        f[i]=(a*f[i-1]+b*f[i-2])%7;
        if(mp.count({f[i], f[i-1]}))
        {
            int idx=mp[{f[i], f[i-1]}];
            int len=i-idx;//i-1-idx+1
            int pos=(n-idx)%len;
            if(pos==0) pos=len;
            return f[idx+pos];
        }
        mp[{f[i], f[i-1]}]=i;
    }
    return f[n];
}
int main()
{
    IOS
    while(cin>>a>>b>>n, a||b||n)
    {
        cout<<find()<<"\n";
    }
    return 0;
}

E 昨日重现

题面大意:求从1到n的连续自然数平方和 对1007取模
巧了,有数学公式(推导过程移步百度?和立方和公式有关,高数课也提到过哈)

a n s = n ∗ ( n + 1 ) ∗ ( 2 ∗ n + 1 ) 6 % 1007 ans=\frac{n*(n+1)*(2*n+1)}{6} \% 1007 ans=6n(n+1)(2n+1)%1007
取模可以先进行哈( 1007 ∗ 1008 ∗ 2015 1007*1008*2015 100710082015 不会爆 int,保险起见无脑开LL也行

#include <bits/stdc++.h>

using namespace std;
#define IOS ios::sync_with_stdio(false), cin.tie(0);
typedef long long LL;

int main()
{
    IOS
    LL n;
    while(cin>>n)
    {
        n%=1007;
        LL ans=n*(n+1)*(2*n+1)/6;
        ans%=1007;
        cout<<ans<<"\n";
    }
    return 0;
}

F n!末尾有多少个0

做法就是求 n! 的质因子5的个数

理论依据: 末尾0是一定由 2 ∗ 5 2*5 25 组合而来(即使数字10也是分解成2*5),由质因数分解唯一性定理,末尾0其实就是min(2, 5)的个数,易知2个数远远大于5的因子个数

Code
#include <bits/stdc++.h>

using namespace std;
#define IOS ios::sync_with_stdio(false), cin.tie(0);

//质因数分解唯一性定理,末尾0其实就是min(2, 5)的个数,易知2个数远远大于5的因子个数
int main()
{
    IOS
    int n;
    while(cin>>n)
    {
        int cnt=0;
        while(n)
        {
            cnt+=n/5;
            n/=5;
        }
        cout<<cnt<<"\n";
    }
    return 0;
}

G Google is Feeling Lucky

题面大意:每组输出 value最大的网站(不唯一)

呃比较简单,题目限定了 1 < = v a l u e < = 100 1<=value<=100 1<=value<=100,二维字符串数组存 string 就行,在线记录最大 value 然后范围遍历输出即可

Code
#include <bits/stdc++.h>

using namespace std;
#define IOS ios::sync_with_stdio(false), cin.tie(0);

//const int N=;
void solve()
{
    vector<vector<string>> a(101);
    int maxv=0;
    for(int i=0; i<10; i++)
    {
        string s;
        int v;
        cin>>s>>v;
        maxv=max(maxv, v);
        a[v].push_back(s);
    }
    for(auto it:a[maxv]) cout<<it<<"\n";
}
int main()
{
    IOS
    int T;
    cin>>T;
    for(int i=1; i<=T; i++)
    {
        cout<<"Case #"<<i<<":\n";
        solve();
    }
    return 0;
}
感谢阅读 End.
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值