AcWing算法提高课数学部分(持续更新中)

筛质数

1.196. 质数距离 - AcWing题库

思路:素数筛+离散化

1)看题目就知道要用到素数筛

2)其中有一个结论:1-r之间的素数不超过sqrt(r)

3)l~r的区间数字太大,但是r-l并不是很大,就转换成0~l-r

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
using namespace std;
#define int long long
const int maxn = 5e4+10;;
const int maxx=1e6+10;
bool vis[maxn]; // 0 为素数 1 为非素数
int tot, phi[maxn], prime[maxn];
int num[maxx]={0};
void CalPhi() {
    vis[1] = 1, phi[1] = 1;
    for (int i = 2; i < maxn; ++i) {
        if (!vis[i]) prime[tot++] = i, phi[i] = i - 1;
        for (int j = 0; j < tot; ++j) {
            if (i * prime[j] > maxn) break;
            vis[i * prime[j]] = 1;
            if (i % prime[j] == 0) {
                phi[i * prime[j]] = phi[i] * prime[j];
                break;
            } else phi[i * prime[j]] = phi[i] * (prime[j] - 1);
        }
    }
}
int p[maxx]={0};
signed main(void){
    CalPhi();
    int a,b;
    while(~scanf("%lld %lld",&a,&b)){
        memset(num,0,sizeof num);
        memset(p,0,sizeof p);
        for(int i=0;i<tot;i++){
            if(prime[i]>=b){
                break;
            }
            for(int j=a/prime[i];j*prime[i]<=b;j++){
                if(j<2){
                    continue;
                }
                num[j*prime[i]-a]++;
            }
        }
        int minn=0,maxp=0;
        int flag=0;
        for(int j=0;j<=b-a;j++){
            if(num[j]==0&&j+a>1){
                p[flag++]=j+a;
            }
        }

        for(int i=1;i+1<flag;i++){
            if(p[i+1]-p[i]>p[maxp+1]-p[maxp]) maxp=i;
            if(p[i+1]-p[i]<p[minn+1]-p[minn]) minn=i;
        }
        if(flag<2){
            printf("There are no adjacent primes.\n");
        }else{
            printf("%lld,%lld are closest, %lld,%lld are most distant.\n",p[minn],p[minn+1],p[maxp],p[maxp+1]);
        }

    }
}
View Code

 

分解质因数

1.197. 阶乘分解 - AcWing题库

思路:

1)打素数表

2)当我们算一个素数i的含有多少个的时候,拆分成计算1-n中的数中有多个数有i,i^2,i^3…,当然我们计算的时候,计算含有这个素数的个数的时候要按照上面样例中求2的方式求,就是横着求,才能够缩短时间复杂度

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
const int maxn = 10000001;
bool vis[maxn]; // 0 为素数 1 为非素数
int tot, phi[maxn], prime[maxn];

void CalPhi() {
    vis[1] = 1, phi[1] = 1;
    for (int i = 2; i < maxn; ++i) {
        if (!vis[i]) prime[tot++] = i, phi[i] = i - 1;
        for (int j = 0; j < tot; ++j) {
            if (i * prime[j] > maxn) break;
            vis[i * prime[j]] = 1;
            if (i % prime[j] == 0) {
                phi[i * prime[j]] = phi[i] * prime[j];
                break;
            } else phi[i * prime[j]] = phi[i] * (prime[j] - 1);
        }
    }
}
int p[maxn]={0};
int main(){
    int n;
    scanf("%d",&n);
    CalPhi();
    int flag=0;
    for(int i=2;i<=n;i++){
        if(vis[i]==0){
            flag=0;
            int s=n;
            int now;
            while(s){
                flag+=s/i;
                s/=i;
            }
            printf("%d %d\n",i,flag);
        }
    }
}
View Code

 

快速幂

1.1290. 越狱 - AcWing题库

思路:补集

1)注意加减的时候,取余需要加一个mod再进行取余,不然会出现负数

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
#define int long long
const int mod=100003;
inline long long Pow(long long a, long long n, long long m) {
    long long t = 1;
    for (; n; n >>= 1, a = (a * a % m))
        if (n & 1) t = (t * a % m);
    return t % m;
}

signed main(void){
    int n,m;
    scanf("%lld %lld",&m,&n);//不越狱m m-1 m-1 ……  总:m*m*m*m*……
    printf("%lld\n",(Pow(m,n,mod)%mod-m*Pow(m-1,n-1,mod)%mod+mod)%mod);

}
View Code

 

约数个数

1.1294. 樱花 - AcWing题库

思路:主要是公式推导,最后推到成看(n!)^2的约数个数(推倒思路见前文章)

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
const int maxx=1e6+10;//约数个数 横向求解的过程
const int maxn = 10000001;
const int mod=1e9+7;
bool vis[maxn]; // 0 为素数 1 为非素数
int tot, phi[maxn], prime[maxn];

void CalPhi() {
    vis[1] = 1, phi[1] = 1;
    for (int i = 2; i < maxn; ++i) {
        if (!vis[i]) prime[tot++] = i, phi[i] = i - 1;
        for (int j = 0; j < tot; ++j) {
            if (i * prime[j] > maxn) break;
            vis[i * prime[j]] = 1;
            if (i % prime[j] == 0) {
                phi[i * prime[j]] = phi[i] * prime[j];
                break;
            } else phi[i * prime[j]] = phi[i] * (prime[j] - 1);
        }
    }
}
int p[maxx]={0};
int main(){
    int n;
    scanf("%d",&n);
    CalPhi();
    long long int s=1;
    for(int i=0;i<tot;i++){
        if(prime[i]<=n){
            int now=n;
            int flag=0;
            while(now){
                flag+=now/prime[i];
                now/=prime[i];
            }
            flag*=2;
            flag%=mod;
            s*=flag+1;
            s%=mod;
            
        }
    }
    printf("%lld\n",s);
}
View Code

2.198. 反素数 - AcWing题库

思路:AcWing 198. 反素数 - AcWing(三个结论)

1)1~n中最大的反质数就是1~n中约数个数最多的数中最小的一个

2)1~n中任何数的不同质因子都不会超过10个,因为2*3*5*7*11*13*17*19*23*29*31>2*10^9,且所有质因数的指数总和不超过30,因为2^31>2e9

3)x的质因数是连续的若干最小的质数,且质数的质数单调递减。如果不连续,例如选择3 7不如选择相同指数个数的2 3,这样的数更小。指数的问题就是,如果质数前面的大后面的小,两者指数交换,得到的数肯定比这个数小

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
#define int long long
int prime[15]={0,2,3,5,7,11,13,17,19,23,29};
int s_cnt=1e9+1,s_num=1;
int c[15];
int n;
void dfs(int now,int cnt,int num){//现在到了哪一个 现在的乘积,现在的约数个数
    if(now==11){
        if((num>s_num)||(cnt<s_cnt&&num==s_num)){
            s_num=num;
            s_cnt=cnt;
        }
        return;
    }
    int now_cnt=cnt;//现在的乘积
    for(int i=0;i<=c[now-1];i++){
        if(now_cnt>n){
            return;
        }
        c[now]=i;
        dfs(now+1,now_cnt,num*(i+1));
        now_cnt*=prime[now];
    }
}
signed main(void){
    scanf("%lld",&n);
    c[0]=2e9;
    dfs(1,1,1);
    printf("%lld\n",s_cnt);
}
View Code

结论:

 

 

欧拉函数

1.220. 最大公约数 - AcWing题库

思路:AcWing 220. 最大公约数(通俗易懂&效率高) - AcWing

主要是转换,通过gcd(x*d,y*d)=d,转换为找gcd(x,y)=1的总数,这样的话先打素数表,再去求欧拉函数的和(为了减少时间复杂度,也可以先利用前缀和进行欧拉函数的求和)

代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
#define int long long
const int maxn = 10000001;
bool vis[maxn]; // 0 为素数 1 为非素数
int tot, phi[maxn], prime[maxn];

void CalPhi() {
    vis[1] = 1, phi[1] = 1;
    for (int i = 2; i < maxn; ++i) {
        if (!vis[i]) prime[tot++] = i, phi[i] = i - 1;
        for (int j = 0; j < tot; ++j) {
            if (i * prime[j] > maxn) break;
            vis[i * prime[j]] = 1;
            if (i % prime[j] == 0) {
                phi[i * prime[j]] = phi[i] * prime[j];
                break;
            } else phi[i * prime[j]] = phi[i] * (prime[j] - 1);
        }
    }
}
signed main(void){
    int n;
    scanf("%lld",&n);
    CalPhi();
    int sum=0;
    for(int i=0;i<tot;i++){
        if(prime[i]<=n){
            int flag=0;
            for(int j=2;j<=n/prime[i];j++){
                flag+=phi[j];
            }
            flag*=2;
            sum+=flag+1;//算上a=b,且为素数
        }

    }
    printf("%lld\n",sum);
}
View Code

 

同余

1.1298. 曹冲养猪 - AcWing题库

思路:裸中国剩余定理AcWing 1298. 曹冲养猪 - AcWing

代码:

#include <iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int N = 15;
int n;
int a[N], m[N];
void exgcd(LL a, LL b, LL &x, LL &y) {
    if (!b) x = 1, y = 0;
    else {
        exgcd(b, a % b, y, x);
        y -= a / b * x;
    }
}
int main() {
    cin >> n;
    LL M = 1;
    for (int i = 0; i < n; ++ i) {
        cin >> m[i] >> a[i];
        M *= m[i];  //读入mi的同时计算M
    }
    LL res = 0;
    for (int i = 0; i < n; ++ i) {
        LL Mi = M / m[i];   //计算Mi = M/mi
        LL ti, y;
        //这一步是求逆元,根据逆元公式的衍生公式可以得到 ti * Mi + y * mi = 1
        exgcd(Mi, m[i], ti, y);
        res += a[i] * Mi * ti;  //计算的同时累加到res中(上述公式里有个sum需要累加)
    }
    cout << (res % M + M) % M << endl;  //对于任意x+kM都是满足要求的解,但目标是输出最小的正整数x,因此取模即可
    return 0;
}
View Code

 

矩阵乘法

1.1303. 斐波那契前 n 项和 - AcWing题库

思路:主要是矩阵的构造

代码:

#include <iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int N = 3;
int n, m;
ll A[N][N] =    // 上述矩阵 A
{
    {2, 0, -1},
    {1, 0, 0},
    {0, 1, 0}
};
ll S[N] = {2, 1, 0}; // 上述矩阵 S(转置)

void multi(ll A[], ll B[][N]) // 计算方阵 B 乘向量 A,并将结果储存在 A 中
{
    ll ans[N] = {0};
    for (int i = 0; i < N; i ++ )
        for (int j = 0; j < N; j ++ )
            ans[i] += A[j] * B[i][j] % m;
    for (int i = 0; i < N; i ++ )
        A[i] = ans[i] % m;
}

void multi(ll A[][N], ll B[][N]) // 计算方阵 A * B,并将结果储存在 A 中
{
    ll ans[N][N] = {0};
    for (int i = 0; i < N; i ++ )
        for (int j = 0; j < N; j ++ )
            for (int k = 0; k < N; k ++ )
                ans[i][j] += A[i][k] * B[k][j] % m;
    for (int i = 0; i < N; i ++ )
        for (int j = 0; j < N; j ++ )
            A[i][j] = ans[i][j] % m;
}

signed main(void) {
    scanf("%d %d",&n,&m);
    while(n){
        if(n&1) multi(S,A);
         multi(A,A);
        //printf("%lld\n",n);
        n>>=1;
    }
    printf("%lld\n",(S[2]%m+m)%m);

}
View Code

2.1304. 佳佳的斐波那契 - AcWing题库

思路:AcWing 1304. 佳佳的斐波那契 - AcWing

主要是构造了一个新的函数,必须构造的新函数让其是线性相关的,不可以有系数,不然就会出现n相关的,这样矩阵就不是常数矩阵了

代码:

#include <iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int N = 4;
int n, m;
ll A[N][N] =    // 上述矩阵 A
{
        {0 , 1 , 0 , 0},
        {1 , 1 , 1 , 0},
        {0 , 0 , 1 , 1},
        {0 , 0 , 0 , 1}
};
ll S[N] = {1, 1, 1, 0}; // 上述矩阵 S(转置)

void _multi(ll A[], ll B[][N]) // 计算方阵 B 乘向量 A,并将结果储存在 A 中
{
    ll ans[N] = {0};
    for (int i = 0; i < N; i ++ )
        for (int j = 0; j < N; j ++ )
            ans[i] += A[j] * B[j][i] % m;
    for (int i = 0; i < N; i ++ )
        A[i] = ans[i] % m;
}

void multi(ll A[][N], ll B[][N]) // 计算方阵 A * B,并将结果储存在 A 中
{
    ll ans[N][N] = {0};
    for (int i = 0; i < N; i ++ )
        for (int j = 0; j < N; j ++ )
            for (int k = 0; k < N; k ++ )
                ans[i][j] += A[i][k] * B[k][j] % m;
    for (int i = 0; i < N; i ++ )
        for (int j = 0; j < N; j ++ )
            A[i][j] = ans[i][j] % m;
}
void get(int n)                  // 求出 X[n]
{
    while (n)
    {
        if (n & 1){
            _multi(S, A);
        }
        multi(A, A);
        n >>= 1;
    }
}
signed main(void) {
    scanf("%d %d",&n,&m);
    get(n-1);
    long long int s=(((n*S[2]-S[3])%m+m)%m);
    //printf("%lld %lld\n",S[2],S[3]);//1 0
    printf("%lld\n",s);

}
View Code

 

组合数学

1.1307. 牡牛和牝牛 - AcWing题库

思路:前缀和+dp,令f[i]表式如果此位是1的话,那么有多少种情况,s[i]主要是前缀和,为了方便计算f[i],还可以计算出最后需要的结果

代码:

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxx=1e5+10;
const int mod=5000011;
int main(){
    long long int a[maxx]={0},s[maxx]={0};
    int n,k;
    scanf("%d %d",&n,&k);
    a[0]=1;
    s[0]=1;
    for(int i=1;i<=n;i++){
        if(i<=k+1){
            a[i]=1;
        }else{
            a[i]=s[i-1-k];
        }
        s[i]+=(s[i-1]+a[i])%mod;
    }
    long long sum=0;
    sum=s[n];
    printf("%lld\n",((sum%mod+mod)%mod));
}
View Code

2.1309. 车的放置 - AcWing题库

思路:主要是切分的方法要记住

 

posted @ 2021-09-01 21:20  bonel  阅读(306)  评论(0编辑  收藏  举报