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(N−1)+…+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)=A∗f(n−1)+B∗f(n−2)%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[i−1]f[i−2] 出现的索引
i
−
1
i-1
i−1,每次更新
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)∗(2∗n+1)%1007
取模可以先进行哈(
1007
∗
1008
∗
2015
1007*1008*2015
1007∗1008∗2015 不会爆 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 2∗5 组合而来(即使数字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;
}