阶乘问题
题意:输出n的阶乘最靠右且不为0的数
题解:一开始我是每次只保留最右一个不为0的数,但是这样子会有误差,其实每次保留不为0最靠右的8位数然后在最后输出的时候保留一位。
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
using namespace std ;
typedef long long ll ;
const int mod = 1e8 ;
int main(){
ll n ; cin >> n ;
ll res = 1 ;
for(int i = 1 ; i <= n ; ++ i){
res *= i ;
while(1){
if(res%10 != 0) {
res=res%mod ;
break ;
}
else res /= 10 ;
}
}
cout << res%10 << endl ;
return 0 ;
}
计算系数
题意:给出a b k n m 求式子(ax+by)k 中 xn × ym的系数,其中n+m=k
题解:其实就是求
∑
k
m
+
1
\sum_{k}^{m+1}
∑km+1*an*bm ,前面的系数用到杨辉三角求,后面就是一个快速幂取模。
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <iostream>
using namespace std ;
typedef long long ll ;
const int mod = 10007 ;
const int N = 1e3 + 5 ;
ll f[N][N] ;
ll fastpow(ll a ,ll n){
ll res = 1 ;
while(n){
if(n&1) res = res*a%mod ;
a = a*a%mod ;
n >>= 1 ;
}
return res ;
}
int main(){
//计算杨辉三角
f[1][1] = f[2][1] = f[2][2] = 1 ;
for(int i = 3 ; i <= 1e3+2 ; ++ i)
for(int j = 1 ; j <= i+1 ; ++ j)
f[i][j] = (f[i-1][j-1]+f[i-1][j])%mod ;
ll a,b,k,n,m ;
cin >>a>>b>>k>>n>>m ;
cout << f[k+1][m+1]*fastpow(a,n)*fastpow(b,m)%mod << endl ;
return 0 ;
}
斐波那契公约数
题意: 求斐波那契数列的第n项和第m项的最大公约数
题解:gcd(fn,fm)= f gcd(n,m),主要是最后一个测试点的数据为1e9会超时,所以要用到矩阵快速幂取模
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std ;
const int N = 2 ;
const int MOD = 1e8 ;
typedef long long ll ;
struct Matrix{
ll m[N][N] ;
Matrix(){
memset(m,0,sizeof(m)) ;
}
};
Matrix multi(Matrix a , Matrix b){
Matrix res ;
for(int i=0 ; i < N ; ++ i)
for(int j=0 ; j<N ; ++j)
for(int k=0;k<N ;++k)
res.m[i][j] = (res.m[i][j] + a.m[i][k]*b.m[k][j])%MOD ;
return res ;
}
Matrix fast(Matrix a , int n){
Matrix res ;
for(int i=0; i<N ; ++i)
res.m[i][i] = 1 ; //初始化单位矩阵
while(n){//快速幂
if(n&1) res=multi(res,a) ;
a = multi(a,a) ;
n >>= 1 ;
}
return res ;
}
int main(){
int a,b ; scanf("%d%d",&a,&b) ;
Matrix t ;
t.m[0][0] = t.m[0][1] = t.m[1][0] = 1 ;
t.m[1][1] = 0 ;
int x = __gcd(a,b) ;
t = fast(t,x) ;
printf ("%d\n",t.m[0][1]) ;
return 0 ;
}
扫雷
题意:一个n×2的格子,右边是每个格子八连格中雷的个数(1~3),问左边的雷的摆放方案有几个
题解:因为雷的个数范围为1~3,第一个格子最少是1所以它的雷要么在第一行要么在第二行,所以第一行分有雷和没有雷(b[1]=1 b[1]=0 , 0表示没有雷,1表示有雷)两种情况讨论。
#include <cstdio>
const int N = 1e4 + 5 ;
int a[N] , b[N] ;
int n ;
bool check(){
for(int i=2 ; i <= n+1 ; ++ i){
b[i] = a[i-1]-b[i-1]-b[i-2] ;
if(!(b[i]==0||b[i]==1)) return false ;
if(i == n+1 && b[i] != 0) return false ;
}
return true ;
}
int main(){
scanf("%d",&n) ;
for(int i = 1 ; i <= n ; ++ i)
scanf("%d",&a[i]) ;
b[1] = 1 ;
int ans = 0 ;
if(check()) ++ans ;
b[1] = 0 ;
if(check()) ++ans ;
printf("%d\n",ans) ;
return 0 ;
}
余数求和
题意:求
∑
i
=
1
n
\sum_{i=1}^{n}
∑i=1nk%i
题解:a%b = a - b*
a
b
\frac{a}{b}
ba ,ans =
∑
i
=
1
n
\sum_{i=1}^{n}
∑i=1n(k-i*
k
i
\frac{k}{i}
ik) = n*k -
∑
i
=
1
n
\sum_{i=1}^{n}
∑i=1ni *
k
i
\frac{k}{i}
ik,如果一个个算k/i那1e9的数据肯定超时,所以在这里用到了除法分块,因为k/i是整数在一个区域块中是相同的所以可以一起算,
每一块的和 = k/i * 块的大小(r-l+1) * i的平均值(l+r)/2
即
∑
i
=
1
n
\sum_{i=1}^{n}
∑i=1ni *
k
i
\frac{k}{i}
ik = k/i * (r-l+1) * (l+r)/2
#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std ;
typedef long long ll ;
int main(){
ll n , k , l , r , t ;
cin >> n >> k ;
ll ans =n*k ;
for(l = 1 ; l <= n ; l=r+1){
t = k/l ; //当前块的值
if(t) r = min(k/t,n); //求右端点
else r = n ;
ans -= t*(r-l+1)*(l+r)/2 ;
}
cout << ans << endl ;
return 0 ;
}
卡牌游戏
题意:从第一个人开始抽牌,牌子上的数往下数到这个数的人出局,最后留下的一个人获胜,求每个人的获胜概率,给出人数n和牌数m,还有每张牌子上的数。
题解:用dp的特性继承上一次的概率模拟计算
#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std ;
const int N = 55 ;
int a[N] ;
double dp[N][N] ;
int main(){
int n,m ; scanf("%d%d",&n,&m) ;
for(int i=1;i<=m;++i) scanf("%d",&a[i]) ;
dp[1][1] = 1.0 ;
for(int i = 2 ; i<=n ; ++i){
for(int k=1 ; k<=m ; ++k){
int p=(a[k]%i==0)?i:a[k]%i ; //第p个人被淘汰
for(int j=1 ; j<=i-1 ; ++j) {
++ p ; //下一个人
if(p > i) p=1 ;
dp[i][p] += dp[i-1][j]/(double)m ;
}
}
}
for(int i=1 ; i<=n ; ++ i)
printf("%.2lf%% ",dp[n][i]*100) ;
return 0 ;
}
2^k进制数
题意:给出k w 表示2k进制下相邻右边的数要大于左边的数且在二进制下长度不大于w
题解:组合数+高精度,详解移步大佬博客—>题解
#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std ;
const int N = 205 ;
int a[30005][N] , c[N] , ans[N] ;
void add(int a[] , int b[]){//高精度加法
int x = 0 ;
a[0] = max(a[0],b[0]) ;
for(int i=1; i<=a[0] ; ++ i){
a[i] += b[i]+x ;
x = a[i]/10 ;
a[i] %= 10 ;
}
if(x>0) a[++a[0]] = x ;
}
int main(){
int k , w ; scanf("%d%d",&k,&w) ;
if(w<=k){
printf("0\n") ;
return 0 ;
}
int mx=(1<<k)-1 ; //每一位数的最大值
int first , len ;
if(w%k==0){
first = mx ;
len = w/k-1 ;
}
else{
first =(1<<(w%k))-1 ;
len = w/k ;
}
a[1][0]=1 , a[1][1]=1 ;
ans[0]=0 ;
for(int i=1 ; i<=mx ; ++i){
for(int j=i+1 ; j>=1 ; -- j)
add(a[j],a[j-1]) ;
if(i>=mx-first && i<mx)
add(ans,a[len+1]) ;
}
for(int i=2 ; i<=len ; ++i)
add(ans,a[i+1]) ;
for(int i=ans[0] ; i>0 ; -- i)
printf("%d",ans[i]) ;
printf ("\n") ;
return 0 ;
}