数论基础(from -- kuangbin)

链接:http://vjudge.net/contest/133425#overview

A.

题意:T组数据,n个数,找到每个数对应的映射满足f(x) >= n,(x最小)f表示欧拉函数,即小于x且与x互素的个数。将x求和,使得sum最小。

解析:首先需要欧拉函数。方法一:然后二分搜索(错误or处理一下遍历一遍将当前点 phi[i] = max(phi[i - 1],phi[i])即可)。方法二:另一种是打表,将每个不同的值中对应的最小x都找出来。每次都更新一段,从上一次更新结束的右一位开始到本次phi[i]结束。都要等于i。保证re[]对应的都是最小的值

代码:

方法一:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <string>
#include <algorithm>
#include <list>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long LL;
int const maxn = 1100000;
int phi[maxn + 5];
void phi_table(int n)
{
    for(int i = 2; i <= n; i ++)
        phi[i] = 0;
    phi[1] = 0;
    for(int i = 2; i <= n; i ++)
    {
        if(!phi[i])
        {
            for(int j = i; j <= n; j += i)
            {
                if(!phi[j])
                {
                    phi[j] = j;
                }
                phi[j] = phi[j] / i * (i - 1);
            }
        }
    }
    for(int i = 1; i <= n; i ++)
    {
        phi[i] = max(phi[i - 1],phi[i]);
    }
}

int main()
{
    //freopen("in.txt","r",stdin);
    phi_table(maxn);
    int T;
    scanf("%d",&T);
    for(int t = 1; t <= T; t ++)
    {
        long long  ans = 0;
        int n,x;
        scanf("%d",&n);
       
        for(int i = 0; i < n; i++)
        {
            scanf("%d",&x);
            ans += lower_bound(phi,phi + maxn,x) - phi;


        }
        printf("Case %d: %lld Xukha\n",t,ans);
    }
    return 0;
}

方法二:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <string>
#include <algorithm>
#include <list>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long LL;
int const maxn = 1100000;
int phi[maxn + 5];
int re[maxn];
void phi_table(int n)
{
    for(int i = 2; i <= n; i ++)
        phi[i] = 0;
    phi[1] = 0;
    for(int i = 2; i <= n; i ++)
    {
        if(!phi[i])
        {
            for(int j = i; j <= n; j += i)
            {
                if(!phi[j])
                {
                    phi[j] = j;
                }
                phi[j] = phi[j] / i * (i - 1);
            }
        }
    }
}

void fun()
{
//only uese the minx places that have been before (it means that with smaller money to get these score)
    int minx = 0;
    for(int i = 0; i < maxn; i ++)
    {
        if(re[phi[i]] == 0)
        {
            for(int j = minx + 1; j <= phi[i] && j <= 1000000; j ++)
            {
                re[j] = i;
            }
            minx = phi[i];
        }
    }

}

int main()
{
   // freopen("in.txt","r",stdin);
    phi_table(maxn);
    fun();
    int T;
    scanf("%d",&T);
    for(int t = 1; t <= T; t ++)
    {
        long long  ans = 0;//pay attention to the long long 
        int n,x;
        scanf("%d",&n);
        for(int i = 0; i < n; i++)
        {
            scanf("%d",&x);
           ans += re[x];

        }
        printf("Case %d: %lld Xukha\n",t,ans);
    }
    return 0;
}
B.

题意:给一个集合,求一个子集。使得子集中的每个元素都不能由其他元素乘以一个素数得到。求子集最大的个数。

分析:每个数所有指数的和为奇数或偶数。这样一个二分图。只有指数和为奇数的,才能到达偶数的。偶数的才能到达奇数的。首先分解质因数。并分类存储他们的坐标。以偶数的出发对奇数的建边,每次除以一个出现的素数。以奇数的出发,除以一个素数对偶数的建边。保证不重复不漏的简历一个有向的二分图。然后二分匹配求最大匹配。用n -   最大匹配数,及最大子集数。

二分匹配中最大独立集=顶点数n-最大匹配
代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <string>
#include <algorithm>
#include <list>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long LL;
int const MAXN = 40005;


#define INF 0x3f3f3f3f


vector<int>prime;

int p[2][500005];
vector<int> G[MAXN],v[MAXN];
int Mx[MAXN],My[MAXN];
int dx[MAXN],dy[MAXN];
int dis;
bool used[MAXN];//DFS中甬道的访问标记

bool isPrime[MAXN];
LL primeList[MAXN],primeCount = 0;
void primeInit(LL n)
{

    memset(isPrime,true,sizeof(isPrime));//初始化认为全部是素数
    int m = sqrt(n + 0.5);
    for(int i = 2; i <= m;  i ++)
    {
        if(isPrime[i])//判断是素数
        {
            for(int j = i * i; j <= n; j += i)
            {

                isPrime[j] = false;
            }
        }
    }
    for(int i = 2; i <= n; i ++)
    {
        if(isPrime[i])
        {

            primeList[primeCount] = i;
            primeCount ++;
        }

    }
}

vector<int>e[MAXN];
int num[MAXN];
void fun(int k,int n)
{
    int m = n;
    int number = 0;
    for(int i = 0; primeList[i] * primeList[i] <= n; i ++)
    {
        if(n % primeList[i] == 0)
        {
            while(n % primeList[i] == 0)
            {
                n = n  / primeList[i];
                number ++;

            }
            e[k].push_back(primeList[i]);
        }
    }
    if(n != 1)
    {

        e[k].push_back(n);
        number ++;
    }
    num[k] = number;
    if(num[k] % 2 == 0)
        p[0][m] = k;
    else
        p[1][m] = k;

}
bool vis[500005];
int a[MAXN];
void unionx(int n)
{
    for(int i = 0; i < n; i ++)
    {
        if(num[i] % 2 == 0)
        {
            for(int j = 0; j < e[i].size(); j ++)
            {
                if(p[1][a[i] / e[i][j]] != -1)
                {
                    G[i].push_back(p[1][a[i] / e[i][j]]);
                }
            }
        }
        else
        {
            for(int j = 0; j < e[i].size(); j ++)
            {
                if(p[0][a[i] / e[i][j]] != -1)
                {
                    G[p[0][a[i] / e[i][j]]].push_back(i);
                }
            }
        }
    }
}
int n;
void init(){
    for(int i = 0; i <= n; i++){
        G[i].clear();
        v[i].clear();
    }
}

bool SearchP(){
    queue<int> q;
    dis = INF;
    memset(dx,-1,sizeof(dx));
    memset(dy,-1,sizeof(dy));
    for(int i = 0 ; i < n; i++){
        if(Mx[i] == -1){
            q.push(i);
            dx[i] = 0;
        }
    }
    while(!q.empty()){
        int u = q.front();
        q.pop();
        if(dx[u] > dis)
            break;
        int l = G[u].size();
        for(int i = 0; i < l; i++){
            int v = G[u][i];
            if(dy[v] == -1){
                dy[v] = dx[u] + 1;
                if(My[v] == -1)
                    dis = dy[v];
                else{
                    dx[My[v]] = dy[v] + 1;
                    q.push(My[v]);
                }
            }
        }
    }
    return dis != INF;
}

bool dfs(int u){
    int l = G[u].size();
    for(int i = 0; i < l; i++){
        int v = G[u][i];
        if(!used[v] && dy[v] == dx[u] + 1){
            used[v] = true;
            if(My[v] != -1 && dy[v] == dis)
                continue;
            if(My[v] == -1 || dfs(My[v])){
                My[v] = u;
                Mx[u] = v;
                return true;
            }
        }
    }
    return false;
}

int MaxMatch(){
    int res = 0;
    memset(Mx, -1, sizeof(Mx));
    memset(My, -1, sizeof(My));
    while(SearchP()){
        memset(used, false, sizeof(used));
        for(int i = 0; i < n; i++)
            if(Mx[i] == -1 && dfs(i))
                res++;
    }
    return res;
}

int main()
{
   // freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    primeInit(10000);
    int T;
    scanf("%d",&T);
    for(int t = 1; t <= T; t ++)
    {
        memset(e,0,sizeof(e));
        memset(p,-1,sizeof(p));
        memset(vis,false,sizeof(vis));
        int ans = 0;
        scanf("%d",&n);
        for(int i = 0; i < n; i++)
        {
            scanf("%d",&a[i]);
            vis[a[i]] = true;
            fun(i,a[i]);
        }
        unionx(n);
       /* for(int i = 0; i < n; i ++)
        {
            for(int j = 0; j < G[i].size(); j ++)
            {
                printf("%d ",G[i][j]);
            }
            printf("\n");
        }*/
        printf("Case %d: %d\n",t, n - MaxMatch());
        for(int i = 0; i < n; i ++)
        {
            G[i].clear();
        }
    }
    return 0;
}

C.

题意:给你一个矩形的面积,和矩形最小边的长度。已知矩形肯定不是正方形。求有多少种方案。(边长都是整数)

解析:分解质因数。将所有质因数的个数 + 1 求积就是所有的分解的方案数(2倍的),由于不能是正方形,直接将方案数 / 2就是所有的矩形方案。

注意是暴力矩形最小边的长度,如果边长b > sqrt(a)  那肯定是不存在的,这样就将方案数变成了10^6种。只要遍历一遍即可

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <string>
#include <algorithm>
#include <list>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long LL;
int const maxn = 2000006;
bool isPrime[maxn];
LL primeList[maxn],primeCount = 0;
//vector<long long>e;// pay attention to the e which is used to store the prime.may be a huge prime
//直接暴力枚举1到最小值中是n的约数的个数即可
void primeInit(LL n)
{

    memset(isPrime,true,sizeof(isPrime));//初始化认为全部是素数
    int m = sqrt(n + 0.5);
    for(int i = 2; i <= m;  i ++)
    {
        if(isPrime[i])//判断是素数
        {
            for(int j = i * i; j <= n; j += i)
            {

                isPrime[j] = false;
            }
        }
    }
    for(int i = 2; i <= n; i ++)
    {
        if(isPrime[i])
        {

            primeList[primeCount] = i;
            primeCount ++;
        }

    }
}
long long  fun(long long n)
{
    int number;
    long long ans= 1;

    for(int i = 0; primeList[i] * primeList[i] <= n; i ++)
    {
        number = 0;
        if(n % primeList[i] == 0)
        {

            while(n % primeList[i] == 0)
            {
                n = n  / primeList[i];
                number ++;
            }
          ans = ans * (number + 1);
        }
    }
    if(n != 1)
    {
       ans = ans * 2;

    }

     return ans;
}



int main()
{
    //freopen("in.txt","r",stdin);
    primeInit(1100000);
    long long n,a;
    int T;
    long long ans= 0;
    scanf("%d",&T);
    for(int t = 1; t <= T; t ++)
    {
        printf("Case %d: ",t);
        scanf("%lld%lld",&n,&a);
        ans = fun(n);
        if(a > sqrt(n) || a * a == n)
        {
            printf("0\n");
            continue;
        }
        else
        {
            ans = ans / 2;
            for(int i = 1; i < a; i ++)
            {
                if(n % i == 0)
                    ans --;
            }
        }
        printf("%lld\n",ans);
    }
    return 0;
}

D.

题意:找从1 到n约数的个数是偶数的个数

解析:奇数 * 偶数 = 偶数,偶数 * 偶数=  偶数,奇数 * 奇数= 奇数。只有当全部的小项都是奇数的时候才是奇数,结果才是奇数。我们统计所有事偶数的个数。

每一项:X =  P^(e+ 1) - 1 / ( P - 1) =( P ^ (e + 1) - P)  / (P - 1) +1 = 1 + P * (P ^ e - 1) / (P - 1)


(P ^ e - 1) / (P - 1)  等于等比数列求和从1开始到 P ^ (e - 1)的和。

分情况啦,如果P是2的话,他一定就是个奇数,最后这一项就肯定是个奇数。

如果P 不是2的话,如果e是偶数,等比数列这一项就是偶数 + 1,X就是奇数。

反之就是偶数,即不是我们要统计的。

既然除了2这一项,每一个质因子都是一个平方,那么可以直接打表找。但是对于乘以2之后,要保证没有算重了的点。只需暴力一遍质因子的平方 * 2,即可

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <string>
#include <algorithm>
#include <list>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long LL;
int main()
{
	//freopen("in.txt","r",stdin);
    int T;

    scanf("%d",&T);
    for(int t = 1; t <= T; t ++)
    {
            long long n,ans = 0;
        scanf("%lld",&n);
        for(long long i = 1; i * i <= n; i ++)
        {
            ans ++;
            if(i * i * 2 <= n)
            {
                ans ++;
            }
        }
       printf("Case %d: %lld\n",t,n - ans);

    }
	return 0;
}

E.

题意:求n的k次方的前三位和后三位。

思路:后三位直接快速幂就可以了。前三位:首先任意一个正数都可以表达为p = 10 ^ a.其中a又等于x + y.x是整数部分,y是小数部分。整数部分显然是增加位数的,我们只需考虑小数部分y,(此处用浮点数的模运算)再将小数部分乘以100就行。坑:保证是三个数!!!!

代码:

//pay attention when you output there will be accurate three num
#include <iostream>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <string>
#include <algorithm>
#include <list>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long LL;
int const mod = 1000;
//x ^m次方,快速幂
long long get_inv(LL x,LL m)
{
    LL r,y;
    for(r = 1,y = m; y; x = x * x % mod,y >>= 1)
    {
        if(y % 2 == 1)
            r = r *  x % mod;
    }
    return (int) r;
}
int main()
{
//freopen("in.txt","r",stdin);
    int T;

    scanf("%d",&T);
    for(int t = 1; t <= T; t ++)
    {
            long long n,k;
        scanf("%lld%lld",&n,&k);
        int ans = (int) (pow(10.0,fmod(log10(n * 1.0) * k * 1.0,1) ) * 100);
       printf("Case %d: %d %03d\n",t,ans,get_inv(n,k));

    }
	return 0;
}

F.

题意:将一个数分为两个素数相加的方案有几种。要求a <= b

解析:线性的素数打表,然后暴力

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <string>
#include <algorithm>
#include <list>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long LL;
int const maxn = 10000002;
int prime[maxn / 10], pNum;
bool notPrime[maxn];
void Prime(int M)
{
    int i, j;
    for(i = 2; i < M; i++) {
        if(!notPrime[i]) { prime[pNum++] = i; }
        for(j = 0; j < pNum && prime[j] * i < M; j++ ) {
            notPrime[prime[j]*i] = 1;
            if(!(i%prime[j]))
                break;
        }
    }
}
int main()
{
//freopen("in.txt","r",stdin);
    int T;
        Prime(10000000);
    scanf("%d",&T);
    for(int t = 1; t <= T; t ++)
    {
        int n,temp;
        int ans = 0;
        scanf("%d",&n);
        for(int i = 0; i < pNum; i ++)
        {
            if(prime[i] > n / 2)
                break;
            temp = n - prime[i];
            if(notPrime[temp] == false)
            {
                ans ++;
            }
        }
       printf("Case %d: %d\n",t,ans);

    }
	return 0;
}

G.

题意:给以个数n,求i从1到n时 n / i的和。

思路:从1到n的每个数的n/ i设为m。

则n / 1 - n/2   为m == 1出现的次数

n /2 - n / 3  m = 2出现的次数,依次类推

m = i的个数20/i - 20/(i + 1)

为了降低复杂度要前面和后面一起进行计算,即从i = 1,到sqrt(n)同时计算n/ i,和减法  求出的个数。注意如果两个计算的是m出现的词数是相同的,则要去重。

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <sstream>
#include <string>
#include <algorithm>
#include <list>
#include <map>
#include <vector>
#include <queue>
#include <stack>
#include <cmath>
#include <cstdlib>
using namespace std;
typedef long long LL;

int main()
{
//freopen("in.txt","r",stdin);
    int T;
    scanf("%d",&T);
    for(int t = 1; t <= T; t ++)
    {
        long long n,ans = 0;
        scanf("%lld",&n);
       for(long long i = 1;i * i <=n ; i ++)
       {
           ans += (n / i - n / (i + 1)) * i + n / i;//n/i - n / (i +1) 表示包括右边边界在内的个数为i的个数
           if(n / i == i)
           {
               ans -= i;
           }
       }
       printf("Case %d: %lld\n",t,ans);

    }
	return 0;
}

H.

题意:给定n,求i,j其中1<=i <= n, i <= j <= n,求i,j的最小公倍数是n 的个数

解析:首先分解质因数,然后对于每一个质因数,设质因数的指数为e。i,j一共有2*e + 1种情况。这些情况中的i和j都是不一样  的。将所有的(2 * e + 1)相乘,最后除了i和j同时为n的,其他的都出现了两次。

代码:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
using namespace std;
typedef long long LL;
int const MAXN = 10000100;
bool isPrime[MAXN];
LL primeList[MAXN/10],primeCount = 0;

void primeInit(long long  n)
{
    memset(isPrime,true,sizeof(isPrime));//先认为都是素数
     isPrime[1]=false;
     long long m= sqrt(n + 0.5);
    for(int i=2;i<=n;i++)
     {
       if(isPrime[i])primeList[primeCount ++]=i;
          for(int j=0;j < primeCount && i*primeList[j]<=n;j++)
         {
            isPrime[i*primeList[j]]=false;
           if(i%primeList[j]==0)break;
         }
     }
}
int e[MAXN / 10],k = 0;
void fenjie(long long n)
{

    int number = 0;
    for(int i = 0; primeList[i] * primeList[i] <= n; i ++)
    {

        if(n % primeList[i] == 0)
        {
            number = 0;
            while(n % primeList[i] == 0)
            {
                n = n  / primeList[i];
                number ++;

            }
            e[k ++] = number;
        }
    }
    if(n != 1)
    {

        e[k ++] = 1;
    }


}
int main()
{
    //freopen("text.in","r",stdin);
    primeInit(10000111);
    //  cout<<primeCount - 1<<endl;
    int T;
    long long n;
    scanf("%d",&T);
    for(int t =1; t <= T; t ++)
    {
        memset(e,0,sizeof(e));
        k = 0;
        scanf("%lld",&n);
        fenjie(n);
        long long ans =1;
        for(int i = 0; i < k; i ++)
        {
            ans = ans * (e[i] * 2 + 1);
        }

        ans = ans / 2 + 1;
        printf("Case %d: %lld\n",t,ans);
    }
    return 0;
}


I.

题意:

求Hn,T组数据个n求Hn

解析:先将所有的T进行排序,按照n从小到大,这样扫一遍暴力就行了

代码:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
struct PP
{
    int id,num;
    bool operator < (const PP x)const
    {
        return num < x.num;
    }
} n[10005];
double ans[10005];

int main()
{
  //  freopen("text.in","r",stdin);
    int T;

    scanf("%d",&T);
    for(int t =1; t <= T; t ++)
    {

        scanf("%d",&n[t].num);
        n[t].id = t;
    }
    sort(n + 1,n + 1 + T);
    double temp = 0,p = 1;
    for(int i = 1; i <= T; i ++)
    {
        for(; p <= n[i].num; p ++)
        {
            temp += 1.0 / p;
        }
        ans[n[i].id] = temp;
    }
    for(int i = 1; i <= T; i ++)
        printf("Case %d: %.10lf\n",i,ans[i]);

    return 0;
}

J.

题意:x = b ^p 求p的最大值

解析:分解质因数,如果当前质因数的指数能够整除上一个则选小的哪个,反之p为1,另外需要注意如果x是个负数,就需要判断p是否为偶数,如果是偶数的话,就要除以2,直到p变为奇数

代码:

//考虑负数 的情况如果是负数而且答案是偶数,则要把答案变成奇数
#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
int const MAXN = 100000 + 100;
bool isPrime[MAXN];
LL primeList[MAXN],primeCount = 0;

void primeInit(long long  n)
{
    memset(isPrime,true,sizeof(isPrime));//先认为都是素数
    isPrime[1]=false;
    for(int i=2; i<=n; i++)
    {
        if(isPrime[i])primeList[primeCount ++]=i;
        for(int j=0; j < primeCount && i*primeList[j]<=n; j++)
        {

            isPrime[i*primeList[j]]=false;
            if(i%primeList[j]==0)break;
        }
    }
}
//分解质因数,接口fenjie(n),质因数的个数存在数组e[i]中
int e[MAXN ],k = 0;
void fenjie(long long n)
{
    memset(e,0,sizeof(e));
    k = 0;
    int number = 0;
    for(int i = 0; primeList[i] * primeList[i] <= n; i ++)
    {

        if(n % primeList[i] == 0)
        {
            number = 0;
            while(n % primeList[i] == 0)
            {
                n = n  / primeList[i];
                number ++;

            }
            e[k ++] = number;
        }
    }
    if(n != 1)
    {

        e[k ++] = 1;
    }

}
int main()
{
 //freopen("text.in","r",stdin);

    primeInit(100000);

    int T;
    long long n;
    scanf("%d",&T);
    for(int t = 1; t <= T; t++)
    {
            bool flag = false;
            bool sf = false;
        scanf("%lld",&n);
        if(n < 0){
            sf = true;
            fenjie(-n);
        }
        else
        fenjie(n);
        int p = -1;
        for(int i = 0; i < k; i ++)
        {
            if(p == -1)
            {
                p = e[i];
            }
            else if(p != e[i])
            {
                if(p < e[i])
                {
                    if(e[i] % p != 0)
                    {
                        flag =true;
                        break;
                    }
                }
                else
                {
                    if(p % e[i] != 0)
                    {
                        flag = true;
                        break;
                    }
                    else
                    {
                        p = e[i];
                    }
                }
            }
        }
        if(flag == true)
        {
            printf("Case %d: 1\n",t);
        }
        else if(sf == false)
        {
            printf("Case %d: %d\n",t,p);
        }
        else
        {
            if(p % 2 == 0)
            {
                while(p % 2 == 0)
                    p = p / 2;
                printf("Case %d: %d\n",t,p);
            }

            else
                printf("Case %d: %d\n",t,p);
        }
    }
    return 0;
}

K.

题意:判断a是否能被b整除。不限正负号

解析:按照白书P314页中的大整数取模来写。就是在将字符串转化为整数的过程中不断的%b,最后判断是否为零即可。

代码:

#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;
char a[200];
int main()
{
  //  freopen("text.in","r",stdin);


    int T;

    scanf("%d",&T);
    int b,ans;
    for(int t = 1; t <= T; t ++)
    {
        scanf("%s%d",a,&b);
        ans = 0;

        for(int i = 0; i < strlen(a); i ++)
        {
            if(a[i] == '-')
                continue;
            ans = (int)(((long long )ans * 10 +a[i] - '0') % b);
        }
        if(ans == 0)
        printf("Case %d: divisible\n",t);
        else
        {
            printf("Case %d: not divisible\n",t);
        }
    }

    return 0;
}

L.

题意:将复杂 度很高的代码

 int res = 0;
        
for( i1 = 0; i1 < n; i1++ ) {
            
for( i2 = 0; i2 < n; i2++ ) {
                
for( i3 = 0; i3 < n; i3++ ) {
                    ...
                    
for( iK = 0; iK < n; iK++ ) {
                        res 
= ( res + A[i1] + A[i2] + ... + A[iK] ) % MOD;
                    
}
                    ...
                
}
            
}
        
}
转化为复杂度很低的代码。。。。

k层循环,对于每一层循环来说,都是进行了pow(n,k),对于每层循环的每一个A[i]出现的次数都是相同的,即对于每一层来说对结果的贡献都是 : pow(n,k) * sum / n = pow (n,k -1) * sum,对于所有层来说结果就是 pow(n,k - 1) * sum *  k,记得mod就可以了

代码:


#include <cstdio>
#include <cstring>
#include <cmath>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long LL;

int cases, caseno = 0;
int n, K, MOD;
int A[1005];
//x ^m次方,快速幂
long long get_inv(LL x,LL m)
{
    LL r,y;
    for(r = 1,y = m; y; x = x * x % MOD,y >>= 1)
    {
        if(y % 2 == 1)
            r = r *  x % MOD;
    }
    return r;
}
int main()
{
    scanf("%d", &cases);
    while( cases-- )
    {
        long long ans = 0;

        scanf("%d %d %d", &n, &K, &MOD);

        for(int i = 0; i < n; i ++)
        {
            scanf("%lld",&A[i]);
            ans = (A[i] + ans) % MOD;
        }
        ans = ans * K % MOD;
        ans = ans * get_inv(n,K - 1) % MOD;

        printf("Case %d: %lld\n", ++caseno, ans);
    }
    return 0;
}

M.

题意:求闭区间[a,b]中素数的个数。区间较小,a,b很大。

解析:利用类似素数筛选法的方法。先进行素数打表。开数组时直接将数组的第一位当成a。详细见代码

代码:

#include<stdio.h>
#include<math.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>

using namespace std;
typedef long long LL;
int const MAXN = 10000100;
bool isPrime[MAXN];
LL prime[MAXN/10],primeCount = 0;

void primeInit(LL n)
{

    memset(isPrime,true,sizeof(isPrime));//初始化认为全部是素数
    int m = sqrt(n + 0.5);
    isPrime[1] = false;
    for(int i = 2; i <= m;  i ++)
    {
        if(isPrime[i])//判断是素数
        {
            for(int j = i * i; j <= n; j += i)
            {

                isPrime[j] = false;
            }
        }
    }
    for(int i = 2; i <= n; i ++)
    {
        if(isPrime[i])
        {

            prime[primeCount] = i;
            primeCount ++;
        }

    }
}  //先用素数筛选法筛选出小范围的素数
bool f[100005];
int qujiansushu(long long a,long long b)
{
    int num = 0;
    int l = b - a;//将a到b转化为从0到b - a
        for(int i = 0 ; i < primeCount && prime[i] * prime[i] <= b ; i++)
        {
            int j = 0;
            if(a % prime[i] != 0)//判断a + j 如果(a + j)% prime[i] != 0,找到离a最近且比a大的位置
                j = j + prime[i] - a % prime[i];
            if(a + j == prime[i])//如果a + j是素数,则找下一个
                j += prime[i];
            for(; j <= l ; j += prime[i])
                f[j] = 1;//从j开始将含prime[i]因子的数标记(即筛除)
        }
        for(int i = 0 ; i <= l ; i++)
            if(!f[i])
                num++;
        if(a == 1)//如果a从1开始,需要减去一个
            num--;
        return num;
}
int main()
{
    int t, a, b, p = 0;
    scanf("%d", &t);
    primeInit(10000000);
    while(t--)
    {
        p++;
        memset(f, 0, sizeof(f));

        scanf("%d%d", &a, &b);

        printf("Case %d: %d\n", p, qujiansushu(a,b));
    }
    return 0;
}
N

题意:已知n!后面零的个数,求n的最小值。

解析:直接二分搜索。二分的效率是logn  再乘以判断零的个数的时间复杂度。由于t的个数不多。比打表快

代码:

//直接二分搜索求最小的值而不是打表。。。。。
#include<stdio.h>
#include<math.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include <iostream>

using namespace std;
int fun(int x)
{
    int ans = 0;
    int temp = 5;
    while(x >= temp)
    {
        ans = ans + x / temp;
        temp = temp  * 5;
    }
    return ans;
}
int lower_bound(int x,int y,int v)
{
    int m;
    while(x <  y)
    {
        m = x + (y - x) / 2;
        if( fun(m) >= v)
        {
            y = m;
        }
        else
            x = m + 1;
    }
    if(fun(x) != v)
        return -1;
    return x;
}
int main()
{

   // freopen("in.txt","r",stdin);

    //cout<<k<<endl;
    int T;
    scanf("%d",&T);
    for(int t = 1; t <= T; t ++)
    {
        printf("Case %d: ",t);
        int x;
        scanf("%d",&x);
        int ans = lower_bound(1,500000015,x) ;
        if(ans != -1)
            printf("%d\n",ans);
        else
        {
            printf("impossible\n");
        }
    }
    return 0;
}

O.

题意:

已知n,求

G=0;
for(i=1;i<N;i++)
for(j=i+1;j<=N;j++)
{
G+=gcd(i,j);
}
解析:首先递推一下,每增加一,就要增加gcd(1,n) + gcd(2,n) ……gcd(n,n).设gcd(x,n) = i,i肯定是n的约数。枚举i,gcd(x,n) = i,gcd(x / i,n / i) = 1,只要求出小于(n / i)且与其互质的个数,即欧拉函数。就能求出gcd(x,n) = i的个数。个数乘约数本身则是对结果的贡献。注意每增加一个点新增的大小就要存在数组f中。因为要保证i是n 的约数,那么n一定是i的倍数(注意此处的n不是葛素的n),打表即可

代码:

#include<stdio.h>
#include<math.h>
#include<string.h>
#include<stdlib.h>
#include<algorithm>
#include <iostream>

using namespace std;
int phi[4000005];

void phi_table(int n)
{
    for(int i = 2; i <= n; i ++)phi[i] = 0;
    phi[1] = 1;
    for(int i = 2; i <= n; i ++)
    {
        if(!phi[i])
        {
            for(int j = i; j <= n; j += i)
            {
                if(!phi[j])
                {
                    phi[j] = j;
                }
                phi[j] = phi[j] / i * (i - 1);
            }
        }
    }
}
long long  ans[4000005];
long long f[4000005];
void init()
{
    for(int i = 1; i < 4000001; i ++)
    {
        for(int j = i + i; j < 4000001; j += i )//注意约数不能是它本身
        {
            f[j] += i * phi[j / i];
        }
    }
    for(int i = 2; i < 4000001; i ++)
    {
        ans[i] = ans[i - 1] + f[i];
    }
}
int main()
{
    //freopen("in.txt","r",stdin);

    phi_table(4000001);
    init();
    int n;
    while(1)
    {
        scanf("%d",&n);
        if(n == 0)
            break;

        printf("%lld\n",ans[n]);

    }
    return 0;
}
P.

题意:给n个除数,每个除数又对应着可以选择的k个余数。输出m个从小到大的同时满足每个除数余数要求的数。

解析:这个题很复杂。首先当选择不同的余数,选择空间较小时将所有的情况都枚举一遍,然后利用中国剩余定理将每种情况得到的数求出来。如果要求输出的总个数比全部的情况得到的值都多,那么则要加上所有除数的成绩。如果枚举的情况非常多,这时,只需寻找n最大,而k最小。即寻找n / k最小的值,然后枚举,并判断是否n个除数都满足。利用set来插入,利用set的.count()来判断该种余数是否是存在的。 values[c].insert(Y[c][i]);values[c].count(n % X[c]);values[c].clear();

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <set>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
int const maxc = 15;
int const maxk = 105;
int const LIMIT = 10000;
//计算逆需要用到的扩展 欧几里得算法

void gcd(LL a,LL b,LL &d,LL &x,LL &y)
{
    if(!b)
    {
        d = a;
        x = 1;
        y = 0;
    }
    else
    {
        gcd(b,a % b,d,y,x);
        y -= x * (a / b);
    }
}

int china (int n, int* s, int* m) {
    LL M = 1, d, y, x = 0;

    for (int i = 0; i < n; i++)
        M *= m[i];

    for (int i = 0; i < n; i++) {
        LL w = M / m[i];
        gcd(m[i], w, d, d, y);
        x = (x + y * w * s[i]) % M;
    }
    return (x+M)%M;
}
int C,X[maxc],k[maxc];
int Y[maxc][maxk];
int a[maxc];
vector<LL> sol;
set <int>values[maxc];
void dfs(int dep)
{
    if(dep == C)
    {
        sol.push_back(china(C,a,X));
    }
    else
    {
        for(int i = 0; i < k[dep]; i ++)
        {
            a[dep] = Y[dep][i];
            dfs(dep + 1);
        }
    }
}
void solve_enum(int s,int bc)
{
    for(int c = 0; c < C; c ++)
    {
        if(c != bc)
        {
            values[c].clear();
            for(int i = 0; i < k[c]; i ++)
            {
                values[c].insert(Y[c][i]);
            }
        }
    }
    for(int t = 0; s != 0; t ++)
    {
        for(int i = 0; i < k[bc]; i ++)
        {
            LL n = (LL)X[bc] * t + Y[bc][i];
            if(n == 0)
                continue;
            bool ok = true;
            for(int c = 0; c < C; c ++)
            {
                if(c != bc)
                {
                    if(!values[c].count(n % X[c]))
                    {
                        ok = false;
                        break;
                    }
                }

            }
            if(ok)
                {
                    printf("%lld\n",n);
                    if(--s == 0)
                        break;
                }
         }
    }
}
void solve_china(int s)
{
    sol.clear();
    dfs(0);
    sort(sol.begin(),sol.end());
    LL M = 1;
    for(int i = 0; i < C; i ++)
    {
        M *= X[i];
    }
    for(int i = 0; s != 0; i ++)
    {
        for(int j = 0; j < sol.size(); j ++)
        {
            LL n = M * i +sol[j];
            if(n > 0)
            {
                printf("%lld\n",n);
                if(--s == 0)
                break;
            }

        }

    }
}
int main()
{
    //freopen("text.in","r",stdin);
    //freopen("out.txt","w",stdout);
    int s;
    while(scanf("%d%d",&C,&s) == 2  )
    {
        if(C == 0 && s == 0)
            break;
        int bestc = 0;
        long long tot = 1;
        for(int i = 0; i < C; i ++)
        {
            scanf("%d%d",&X[i],&k[i]);
            tot *= k[i];
            for(int j = 0; j < k[i]; j ++)
            {
                scanf("%d",&Y[i][j]);

            }
            sort(Y[i],Y[i] + k[i]);
            if(k[i] * X[bestc] < k[bestc] * X[i])
                bestc = i;
        }


            solve_enum(s,bestc);

        printf("\n");
    }
    return 0;
}


Q.

题意:n * m 大小的网格,其中上下相邻的格子的颜色是不相同的。给定k中颜色,和mod。另外给出一些点,这些点是不能染色的。已知m,k,mod,和mod后一共有几种结果,求n的最小 值。

解析:首先求出如果只有给出点最大的n,求出result看是否成立。这时满足,如果不是最后一行,且它的下面是空的,那么染k种颜色的个数 + 1。如果是第一行被占,那么染k种颜色的个数 -1。再判断只增加一行时候的结果。这时因为可能有在max行上的占用点,有些点染色的种类有k种。之后再增加一行颜色的不同方法肯定是乘以(k- 1)^m次方。将前面的全部设为A,那么就是A* (k - 1) ^m ^add = re(% mod).除以A的逆元, 然后在mod的条件下求log 以(k - 1) ^m次方为敌,re为对数的值

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <set>

#include <map>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
LL const mod = 100000007;
LL xa[505],ya[505];
//x ^m次方,快速幂
    //x ^m次方,快速幂
    long long get_inv(LL x,LL m)
    {
        LL r,y;
        for(r = 1,y = m; y; x = x * x % mod,y >>= 1)
        {
            if(y % 2 == 1)
                r = r *  x % mod;
        }
        return r;
    }
//接口:inv(a,n) a : 待求逆的数,n表示mod
void gcd(LL a,LL b,LL &d,LL &x,LL &y)
{
    if(!b)
    {
        d = a;
        x = 1;
        y = 0;
    }
    else
    {
        gcd(b,a% b,d,y,x);
        y-= x * (a / b);
    }
}
LL inv(LL a,LL n)
{
    LL d,x,y;
    gcd(a,n,d,x,y);
    return d == 1 ? (x + n) % n : -1;
}

LL log_mod (LL a,LL b,LL n)
{
    LL m,v,e=1,i;
    m=(LL)sqrt(n+0.5);
    v=inv(get_inv(a,m),n);
    map<int,int>x;
    x[1]=0;
    for(i=1; i<m; i++)
    {
        e=(e*a)%n;
        if(!x.count(e))x[e]=i;
    }
    for(i=0; i<m; i++)
    {
        if(x.count(b))return i*m+x[b];
        b=(b*v)%n;
    }
    return -1;
}
set<pair<LL,LL> >mm;
//注意就没有pow!!!
int main()
{

//freopen("in.txt","r",stdin);
    int T;
    long long  n,k,b,r;
    scanf("%d",&T);
    for(int t = 1; t <= T; t ++)
    {
        mm.clear();
        long long s = 0;
        long long  m = 1;
        printf("Case %d: ",t);
        scanf("%lld%lld%lld%lld",&n,&k,&b,&r);
        for(int i = 0; i < b; i ++)
        {
            scanf("%lld%lld",&xa[i],&ya[i]);
            m = max(m,xa[i]);
            mm.insert(make_pair(xa[i],ya[i]));
        }
        long long ans =m;
        s += n;
        for(int i = 0; i < b; i ++)
        {
            if(xa[i] != m && !mm.count(make_pair(xa[i] + 1,ya[i])))
            {
                s ++;
            }
            if(xa[i] == 1)
            {
                s --;
            }

        }
        long long A = get_inv(k,s) % mod * get_inv(k - 1,m * n  - b - s) % mod;
        A = A % mod;

        if(A != r)
        {
            int temp = 0;
            for(int i = 0; i < b; i ++)
            {
                if(xa[i] == m)
                {
                    temp ++;
                }
            }
            A=(A*get_inv(k-1,n-temp))%mod;
            A = (A * get_inv(k,temp)) % mod;
            A = A % mod;
            ans ++;
            if(A != r)
            {
                r = r * inv(A,mod) %mod;
                ans = ans  + log_mod(get_inv(k - 1,n),r,mod);
            }
        }

        printf("%lld\n",ans );
    }
    return 0;
}

R.

题意:青蛙A,B沿着赤道都向西跑。青蛙A从x出发,每次跳m米,青蛙B从y出发,每次跳n米。赤道长度为l,问两只青蛙能否跳到一个点上,若能至少跳几次?

解析:列方程x + mt = y + nt (mod l)

直接求解同余模方程。x - y = (n - m) *t + kl,移项得到(m - n)*t - kl = y - x.直接利用扩展欧几里得算法,其中a = m - n,b = k,求解得到x即为t的一个右侧为gcd的解,注意如果右侧(y - x)不能整除gcd则无解,一个解x = 为右侧除gcd * x。然后要求t的最小非负正数解,需要借助一个中间变量p ,p = mod / d.如果p是负数,p = -p.然后只需t = (t %p + p) %d即可

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;

void gcd(LL a,LL b,LL &d,LL &x,LL &y)
{
    if(!b)
    {
        d = a;
        x = 1;
        y = 0;
    }
    else
    {
        gcd(b,a% b,d,y,x);
        y-= x * (a / b);
    }
}


int main()
{

   // freopen("in.txt","r",stdin);
   // freopen("out.txt","w",stdout);
    LL xx,yy,m,n,l;

    while(scanf("%lld%lld%lld%lld%lld",&xx,&yy,&m,&n,&l)!= EOF)
    {
        LL  d,x,y;
        LL A = m - n,B =  yy - xx;

        gcd(A,l,d,x,y);
        if(B % d != 0)
        {
            printf("Impossible\n");
        }

        else
        {
            x = x * B / d;
            if(d < 0)
                d = -d;
            l = l / d;
            x = (x % l + l )%l ;
            printf("%lld\n",x);
        }
    }
    return 0;
}

S.

题意:

for (variable = A; variable != B; variable += C)
给A,B,C以及mod2 ^k问进行几次操作后可以到达C

解析:列方程:A + Ct == B % mod

移项:Ct - nmod = B - A

另a = c,b = mod,求解t,注意如果右侧(B- A)不能整除gcd则无解,一个解t = 为右侧除gcd * t再利用中间值p = mod / d,计算最小非负正数解x = (x % p + p) % p

代码:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <set>
#include <map>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;

void gcd(LL a,LL b,LL &d,LL &x,LL &y)
{
    if(!b)
    {
        d = a;
        x = 1;
        y = 0;
    }
    else
    {
        gcd(b,a% b,d,y,x);
        y-= x * (a / b);
    }
}


int main()
{

  //  freopen("in.txt","r",stdin);
   // freopen("out.txt","w",stdout);
    LL a,b,c,mod;
    while(1)
    {
        scanf("%lld%lld%lld%lld",&a,&b,&c,&mod);
        if(a == 0 && b == 0 && c == 0 && mod == 0)
            break;
        mod = (LL)1<<mod;
        long long d,x,y;
        gcd(c,mod,d,x,y);

        if((b - a) % d != 0 ||( (a != b) && c == 0))
        {
            printf("FOREVER\n");
        }

        else
        {
            x = x * (b - a) / d;
            if(d != 0 &&mod % d == 0)
            mod = mod / d;
            x = (x % mod + mod )% mod;
            printf("%lld\n",x);
        }
    }
    return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值