zoukankan      html  css  js  c++  java
  • [kuangbin带你飞]专题十四 数论基础

     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     


    111 / 423 Problem A LightOJ 1370 Bi-shoe and Phi-shoe

    d.给出n个数a1,a2,...,an,对每个数ai分别找到一个相应的数pi,使其欧拉函数值Φ(pi)>=ai,求p1+p2+...+pn的最小值。

    s.不知道为什么pi要选大于ai的第一个素数。

    #include<iostream>
    #include<stdio.h>
    #include<string.h>
    #include<math.h>
    using namespace std;
    
    const int MAXN=1000100;
    
    bool isPrime[MAXN];
    
    void sieve(int n){
        memset(isPrime,true,sizeof(isPrime));
        isPrime[0]=isPrime[1]=false;
        int i,j,k;
        k=sqrt(n);
        for(i=2;i<=k;++i){
            if(isPrime[i]==true){
                for(j=i+i;j<=n;j=j+i){
                    isPrime[j]=false;
                }
            }
        }
    }
    
    int main(){
    
        sieve(MAXN-1);
    
        int T;
        int n;
        int t;
        int i;
        int j;
        long long sum;
        int ca=0;
    
        scanf("%d",&T);
    
        while(T--){
            scanf("%d",&n);
            sum=0;
            for(i=0;i<n;++i){
                scanf("%d",&t);
                for(j=t+1;j<MAXN;++j){
                    if(isPrime[j]==true){
                        break;
                    }
                }
                sum+=j;
            }
            printf("Case %d: %lld Xukha
    ",++ca,sum);
        }
    
        return 0;
    }
    View Code

    21 / 74 Problem B LightOJ 1356 Prime Independence

    题意:
    找出一些数字的最大质独立集,就是集合能的所有数互相之间不会出现 a[i]==t*a[j] (t是质数) 的情况。
    思路:
    首先想最大独立集对于一般图是NP问题,通常只有求二分图最大独立集,然后就是如何把这些数字分为二分图。
    能够想到如果一个数字等于另一个数字乘以一个质数,那么这两个数字的质因子分解应该只有这一个质数的差别。也就是只会多一个质数,数字上限只有500000,完全可以看一个数组储存某个数字是否存在,然后对每个数字分解质因子,找到他对于每个质因子能够找到的那个数,存在则加边,然后就把质因子总个数(可重复的)是奇数与偶数的分成两边并且把只有一个质因子差别的连通,然后二分图匹配求最大独立集即可。

    即二分图左边为质因子总数为奇数的,右边为质因子总数偶数的。
    关于二分图匹配因为数量较大所以用匈牙利会tle,要用Hopcroft-Carp:

    c.根据这个思路写了下,用的匈牙利O(VE),没超时。不过Hopcroft-Carp这个会快1s左右。

    /*
    //顶点编号从1开始的
    用STL中的vector建立邻接表实现匈牙利算法
    效率比较高
    处理点比较多的效率很高。1500的点都没有问题
    */
    #include<iostream>
    #include<stdio.h>
    #include<algorithm>
    #include<string.h>
    #include<vector>
    #include<math.h>
    using namespace std;
    
    const int MAXN=40000+10;//这个值要超过两边个数的较大者,因为有linker
    int linker[MAXN];
    bool used[MAXN];
    vector<int>G[MAXN];
    int uN;
    bool dfs(int u)
    {
        int sz=G[u].size();
        for(int i=0; i<sz; i++)
        {
            if(!used[G[u][i]])
            {
                used[G[u][i]]=true;
                if(linker[G[u][i]]==-1||dfs(linker[G[u][i]]))
                {
                    linker[G[u][i]]=u;
                    return true;
                }
            }
        }
        return false;
    }
    
    int hungary()
    {
        int u;
        int res=0;
        memset(linker,-1,sizeof(linker));
        for(u=0; u<uN; u++)
        {
            memset(used,false,sizeof(used));
            if(dfs(u)) res++;
        }
        return res;
    }
    
    const int MAXN2=500000+10;
    
    bool exist[MAXN2];//标记数是否存在
    int a[MAXN];//原来的数
    int myHash[MAXN2];//离散化
    
    int factors[MAXN2][2];//[0]存质因子,[1]存个数
    int factCnt;//不同的质因子总个数
    int factCnt2;//包含相同的质因子的总个数
    
    void getFactors(int n){
        int i,k;
        factCnt=0;
        factCnt2=0;
        for(i=2,k=sqrt(n);i<=k;++i){
            if(n%i==0){
                factors[factCnt][0]=i;
                factors[factCnt][1]=1;
                ++factCnt2;
                n=n/i;
                while(n%i==0){
                    ++factors[factCnt][1];
                    ++factCnt2;
                    n=n/i;
                }
                ++factCnt;
                k=sqrt(n);//循环条件不直接写i<=sqrt(n);是因为这样可以避免重复开跟方
            }
        }
        if(n>1){
            factors[factCnt][0]=n;
            factors[factCnt][1]=1;
            ++factCnt;
            ++factCnt2;
        }
    }
    
    int main(){
        int T;
        int N;
        int i;
        int j;
        int tmp;
        int ans;
        int ca=0;
    
        scanf("%d",&T);
    
        while(T--){
            scanf("%d",&N);
            uN=N;//左边集合个数
            for(i=0;i<uN;++i){//记得要清空
                G[i].clear();
            }
    
            memset(exist,false,sizeof(exist));
            for(i=0;i<N;++i){
                scanf("%d",&a[i]);
                exist[a[i]]=true;
                myHash[a[i]]=i;//离散化,二分图N个点就可以了
            }
    
            for(i=0;i<N;++i){
                getFactors(a[i]);//获取质因子
                for(j=0;j<factCnt;++j){//枚举质因子
                    tmp=a[i]/factors[j][0];
                    if(exist[tmp]==true){//tmp存在
                        if(factCnt2&1){//第i个数质因数个数为奇数,第i个数作为左边的点插入一条由左指向右的有向边
                            G[i].push_back(myHash[tmp]);
                        }
                        else{
                            G[myHash[tmp]].push_back(i);
                        }
                    }
                }
            }
            ans=hungary();
            printf("Case %d: %d
    ",++ca,N-ans);
        }
    
        return 0;
    }
    View Code

    c2.Hopcroft-Carp,这个比较快,O(squt(n)*E)

    /*
    //顶点编号从0开始的
    二分图匹配(Hopcroft-Karp算法)
    复杂度O(squt(n)*E)
    邻接表存图,vector实现
    vector先初始化,然后加入边
    uN为左端的顶点数,使用前赋值(点编号0开始)
    */
    #include<iostream>
    #include<stdio.h>
    #include<vector>
    #include<queue>
    #include<string.h>
    #include<math.h>
    using namespace std;
    
    const int MAXN=40000+10;
    const int INF=0x3f3f3f3f;
    vector<int>G[MAXN];
    int uN;
    int Mx[MAXN],My[MAXN];
    int dx[MAXN],dy[MAXN];
    int dis;
    bool used[MAXN];
    bool SearchP(){
        queue<int>Q;
        dis=INF;
        memset(dx,-1,sizeof(dx));
        memset(dy,-1,sizeof(dy));
        for(int i=0;i<uN;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 sz=G[u].size();
            for(int i=0;i<sz;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 sz=G[u].size();
        for(int i=0;i<sz;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<uN;i++)
                if(Mx[i]==-1&&DFS(i))
                    res++;
        }
        return res;
    }
    
    const int MAXN2=500000+10;
    
    bool exist[MAXN2];//标记数是否存在
    int a[MAXN];//原来的数
    int myHash[MAXN2];//离散化
    
    int factors[MAXN2][2];//[0]存质因子,[1]存个数
    int factCnt;//不同的质因子总个数
    int factCnt2;//包含相同的质因子的总个数
    
    void getFactors(int n){
        int i,k;
        factCnt=0;
        factCnt2=0;
        for(i=2,k=sqrt(n);i<=k;++i){
            if(n%i==0){
                factors[factCnt][0]=i;
                factors[factCnt][1]=1;
                ++factCnt2;
                n=n/i;
                while(n%i==0){
                    ++factors[factCnt][1];
                    ++factCnt2;
                    n=n/i;
                }
                ++factCnt;
                k=sqrt(n);//循环条件不直接写i<=sqrt(n);是因为这样可以避免重复开跟方
            }
        }
        if(n>1){
            factors[factCnt][0]=n;
            factors[factCnt][1]=1;
            ++factCnt;
            ++factCnt2;
        }
    }
    
    int main(){
        int T;
        int N;
        int i;
        int j;
        int tmp;
        int ans;
        int ca=0;
    
        scanf("%d",&T);
    
        while(T--){
            scanf("%d",&N);
            uN=N;//左边集合个数
            for(i=0;i<uN;++i){//记得要清空
                G[i].clear();
            }
    
            memset(exist,false,sizeof(exist));
            for(i=0;i<N;++i){
                scanf("%d",&a[i]);
                exist[a[i]]=true;
                myHash[a[i]]=i;//离散化,二分图N个点就可以了
            }
    
            for(i=0;i<N;++i){
                getFactors(a[i]);//获取质因子
                for(j=0;j<factCnt;++j){//枚举质因子
                    tmp=a[i]/factors[j][0];
                    if(exist[tmp]==true){//tmp存在
                        if(factCnt2&1){//第i个数质因数个数为奇数,第i个数作为左边的点插入一条由左指向右的有向边
                            G[i].push_back(myHash[tmp]);
                        }
                        else{
                            G[myHash[tmp]].push_back(i);
                        }
                    }
                }
            }
            ans=MaxMatch();
            printf("Case %d: %d
    ",++ca,N-ans);
        }
    
        return 0;
    }
    View Code

    61 / 332 Problem C LightOJ 1341 Aladdin and the Flying Carpet

    题意:给个矩形的面积a,和矩形的最小边长b,问有多少种矩形的方案(不能是正方形)

    分析:a可以写成x,y,因为不能是正方形,所以设x<y,那么x<sqrt(a),y>sqrt(a)

            所以找到所有小于sqrt(a)的因子,看有几个大于等于b的就是方案数

            因子可以由数的唯一分解定理,求得

    具体 : 先筛一遍1e6以内的素数,有线性筛,然后分解a,然后dfs找所有的小于sqrt(a)的因子,

              由于前12个素数的乘积大于1e12了,所以这部分复杂度,大概是O(2^12)(一般还要略大,不过大不了多少,数组要开大)左右

             可以用这个估计(因为是求小于sqrt(a)的,可以除以2,当然这是空间常数)

              所以这部分复杂度是O(T*2^12)满的话(4000*4000)大概也就是几百万,这部分可以忽略不计

              主要的复杂度在分解素数里,因为1e6里面大概有7w多素数,这部分复杂度(最坏的情况a是大素数),大概是4000*70000,可以卡过,由于不可能都是这种数据

              所以还是可以过的

    吐槽:然后我看了看网上的代码,都是先求出总的,然后暴力扫b减,结果居然过了,b是sqrt(a)的级别,是百万,4000*1e6,是4e9,TLE

            出题人太良心,没有卡这种的QAQ,感觉略坑啊

    #include <cstdio>
    #include <iostream>
    #include <ctime>
    #include <vector>
    #include <cmath>
    #include <map>
    #include <queue>
    #include <algorithm>
    #include <cstring>
    using namespace std;
    typedef long long LL;
    const int N=1e6+5;
    const int INF=0x3f3f3f3f;
    int cnt;
    bool v[N];
    LL prime[80000];
    void getprime(){
      for(int i=2;i*i<=N-5;++i)
        if(!v[i])
          for(int j=i*i;j<=N-5;j+=i)
            v[j]=1;
      for(int i=2;i<=N-5;++i)
      if(!v[i])prime[++cnt]=i;  
    }
    vector<LL>fac[2];
    int divisors[5000],tot;
    LL k;
    void dfs(int pos,LL res){
       if(pos==fac[0].size()){
          divisors[++tot]=res;
          return;
       }
       for(LL i=0,now=1;i<=fac[1][pos];now*=fac[0][pos],++i){
         if(now*res>=k)break;
         dfs(pos+1,res*now);
       }
    }
    int main()
    {
        getprime();
        int cas=0,T;
        scanf("%d",&T);
        while(T--){
           printf("Case %d: ",++cas);
           LL a,b;
           scanf("%lld%lld",&a,&b);
           k=sqrt(a);
           if(k*k!=a)++k;
           if(b>=k){
            printf("0
    ");
            continue;
           }
           LL t=a;
           fac[0].clear(),fac[1].clear();
           for(int i=1;i<=cnt&&prime[i]*prime[i]<=t;++i){
             if(t%prime[i])continue;
             int tmp=0;
             fac[0].push_back(prime[i]);
             while(t%prime[i]==0)++tmp,t/=prime[i];
             fac[1].push_back(tmp);
           }
           if(t>1){
            fac[0].push_back(t);
            fac[1].push_back(1);
           }
           tot=0;
           dfs(0,1);
           int ans=0;
           for(int i=1;i<=tot;++i)
              if(divisors[i]>=b)++ans;
          printf("%d
    ",ans);
        }
        return 0;
    }
    View Code

    法2:根据唯一分解定理,先将a唯一分解,则a的所有正约数的个数为num = (1 + a1) * (1 + a2) *...(1 + ai),这里的ai是素因子的指数,见唯一分解定理,因为题目说了不会存在c==d的情况,因此num要除2,去掉重复情况,然后枚举小于b的a的约数,拿num减掉就可以了

    ps:这个除2很刁。如果存在c==d的情况,会给舍掉。正好符合题意不能是正方形。

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #define ll long long
    using namespace std;
    int const MAX = 1e6 + 10;
    int p[MAX];
    bool u[MAX];
    int num, cnt;
    ll a, b, tmp;
    
    void get_prime()  
    {  
        memset(u, false, sizeof(u));
        for(int i = 2; i <= sqrt(MAX); i++)
            if(!u[i])
                for(int j = i * i; j <= MAX; j += i)
                    u[j] = true;
        for(int i = 2; i <= MAX; i++)
            if(!u[i])
                p[cnt ++] = i;
    }
    
    void cal()
    {
        for(int i = 0; i < cnt && p[i] <= sqrt(tmp); i++)
        {
            int cc = 0;
            while(tmp % p[i] == 0)
            {
                cc ++;
                tmp /= p[i];
            }
            num *= (cc + 1);
    
        }
        if(tmp > 1) //如果tmp不能被整分,说明还有一个素数是它的约数,此时cc=1
            num *= 2;
    }
    
    int main()
    {
        int T;
        scanf("%d", &T);
        cnt = 0;
        get_prime();
        for(int ca = 1; ca <= T; ca++)
        {
            scanf("%lld %lld", &a, &b);
            if(a < b * b)
                printf("Case %d: 0
    ", ca);
            else
            {
                num = 1;
                tmp = a;
                cal();
                num /= 2;
                for(int i = 1; i < b; i++)
                    if(a % i == 0)
                        num --;
                printf("Case %d: %d
    ", ca, num);
            }
        }
    }
    View Code

    54 / 82 Problem D LightOJ 1336 Sigma Function

    题意:定义f(n)为n的约数之和。比如f(6)=1+2+3+6=12。给出n,求[1,n]中f值为偶数的数的个数。

    c.这个代码,,很尴尬,看代码吧。

    #include<iostream>
    #include<stdio.h>
    #include<math.h>
    using namespace std;
    
    int main(){
    
        int T;
        long long n;
        int a,b;
        int ca=0;
    
        scanf("%d",&T);
    
        while(T--){
            scanf("%lld",&n);
            a=sqrt(n);
            b=sqrt(n/2);
            printf("Case %d: %lld
    ",++ca,n-a-b);
        }
    
        return 0;
    }
    View Code

    正规点的做法:看了这个好像明白上面那个了。。。from:http://www.hardbird.net/lightoj-1336-sigma-function/

    大意:定义函数σ(x)为x的所有因子的和。问题是,给一个n,问有多少个σ(x)为偶数,x<=n。

    a1

    思路:可以计算有多少个σ(x)为奇数,用n减去就好了。题目中给出了提示σ(n)可以写成

    a2

    其中单独的一项,是等比数列求和的公式。如果要求σ(n)为奇数,那公式中的每一项乘数都得是奇数。

    对于其中的第i项乘数f(i)=pi^0+pi^1+…+pi^ei.
    当pi=2时,f(i)一定是奇数。
    当pi!=2时,由于pi是奇数,只有当ei为偶数时,才能保证f(i)为奇数。

    所以,当所有的σ(x)为奇数时,x只有两种情况:
    1、所有的ei都为偶数,x就一定是平方数。
    2、除了素因子2的指数为奇数外,所有的ei都为偶数,x就一定是平方数的2倍。

    #include <cstdio>
    using namespace std;
    typedef long long LL;
     
    int main()
    {
        int T, kase=0;
        LL n;
        scanf("%d", &T);
        while(T--)
        {
            scanf("%lld", &n);
            LL ans=0;
            for(LL i=1; i*i<=n; i++)
            {
                ans++;
                if(2*i*i<=n)
                    ans++;
            }
            printf("Case %d: %lld
    ", ++kase, n-ans);
        }
        return 0;
    }
    View Code

    解3:

    题意:求[1,n]中约数和为偶数的数的个数

    思路:根据算术基本定理的约数和公式f(n)=(1+p1+p1^2+...+p1^k1)(1+p2+p2^2+...+p2^k2)...(1+pn+pn^3+...+pn^kn);

    其中n=(p1^k1)*(p2^k2)*...*(pn^kn);(分解素因数)

    由于奇数*奇数还是奇数,奇数*偶数或者偶数相乘是偶数。而素数除了2都是奇数。

    f(n)的奇偶性取决于每个因子的奇偶性,只要出现一个因子是偶数时f(n)为偶数。

    对每个因子,1+p+p^2+...+p^k的奇偶性,

        p为素数,当p是2时,式子为奇数;

                    当p不是2时,p是奇数,p^i为奇数,式子总共k+1项,即k+1个奇数的和,k为偶数时式子为奇数。

    以此条件反推dfs出所有的f(n)为奇数的n,方法是每次乘以奇素数素数的两倍或者2,得到的n就是f(n)为奇数的n了。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<cstdlib>
    #include<algorithm>
    #include<vector>
    #include<stack>
    #include<queue>
    #include<set>
    #include<map>
    #include<string>
    #include<math.h>
    #include<cctype>
    
    using namespace std;
    
    typedef long long ll;
    const int maxn=1000100;
    const int INF=(1<<29);
    const double EPS=0.0000000001;
    const double Pi=acos(-1.0);
    
    ll n;
    vector<ll> odd;
    bool isprime[maxn];
    vector<int> prime;
    const ll M=1000000000010;
    
    void play_prime()
    {
        memset(isprime,1,sizeof(isprime));
        isprime[1]=0;
        for(int i=2;i<maxn;i++){
            if(!isprime[i]) continue;
            for(int j=i*2;j<maxn;j+=i){
                isprime[j]=0;
            }
        }
        for(int i=1;i<maxn;i++){
            if(isprime[i]) prime.push_back(i);
        }
    }
    
    void dfs(int dep,ll cur)
    {
        odd.push_back(cur);
    
        for(int i=dep;i<prime.size();i++){
            ll t=prime[i];
            if(t==2){
                if(cur<=M/2) dfs(i,cur*2);
                else return;
            }
            else{
                if(cur<=M/(t*t)) dfs(i,cur*t*t);
                else return;
            }
        }
    }
    
    int main()
    {
        int T;cin>>T;
        play_prime();
        dfs(0,1);
        sort(odd.begin(),odd.end());
        int tag=1;
        while(T--){
            cin>>n;
            ll ans=upper_bound(odd.begin(),odd.end(),n)-odd.begin();
            printf("Case %d: %lld
    ",tag++,n-ans);
        }
        return 0;
    }
    View Code

    66 / 181 Problem E LightOJ 1282 Leading and Trailing

    后三位直接用快数幂取余可以求出

    前三位我们可以将n^k转化成a.bc * 10^m,这样abc就是前三位了,n^k =  a.bc * 10^m

    即lg(n^k) = lg(a.bc * 10^m)

    <==>k * lg(n) = lg(a.bc) + lg(10^m) = lg(a.bc) + m

    m为k * lg(n)的整数部分,lg(a.bc)为k * lg(n)的小数部分

    x = lg(a.bc) = k * lg(n) - m = k * lg(n) - (int)(k * lg(n))

    a.bc = pow(10, x);

    abc = a.bc * 100;

    这样前三位数abc便可以求出

    #include<iostream>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    #include<list>
    #include<cmath>
    #include<vector>
    using namespace std;
    long long POW(long long a,long long b,long long c){
        if(b==0)return 1;
        long long t=POW(a,b>>1,c);
        t=t*t%c;
        if(b&1)t=t*a%c;
        return t;
    }
    int main()
    {
        long long n,k,i,j,t,test=1;
        scanf("%lld",&t);
        while(t--){
            scanf("%lld%lld",&n,&k);
            double m=k*log10(n)-(long long)(k*log10(n));
            m=pow(10.0,m);
            printf("Case %lld: %lld %03lld
    ",test++,(long long)(m*100),POW(n,k,1000));
        }
        return 0;
    } 
    View Code

    65 / 216 Problem F LightOJ 1259 Goldbach`s Conjecture

    题意:给你一个偶数n,让你求它有多少种可能是两个素数相加得到的

    思路:将10000000以内的素数打表,在开一个标志函数,要注意内存,用bool类型

    /*
    筛法求素数
    */
    #include<iostream>
    #include<stdio.h>
    #include<math.h>
    #include<string.h>
    using namespace std;
    
    const int MAXN=1e7+10;
    
    bool a[MAXN];
    int prime[MAXN/10];
    int primeCnt;//素数个数
    void sieve(int n){//
        memset(a,true,sizeof(a));
        int i,j,k;
        k=sqrt(n);
        a[1]=false;
        for(i=2;i<=k;++i)
            if(a[i]==true){
                for(j=i+i;j<=n;j=j+i)
                    a[j]=false;
            }
        primeCnt=0;
        for(i=2;i<=n;++i){
            if(a[i]==true){
                prime[primeCnt++]=i;
            }
        }
    }
    
    int main(){
    
        sieve(MAXN);
    
        int T;
        int n;
        int i;
        int k;
        int sum;
        int ca=0;
    
        scanf("%d",&T);
    
        while(T--){
            scanf("%d",&n);
            k=n/2;
            sum=0;
            for(i=0;prime[i]<=k;++i){
                if(a[prime[i]]==true&&a[n-prime[i]]==true){
                    ++sum;
                }
            }
            printf("Case %d: %d
    ",++ca,sum);
        }
    
        return 0;
    }
    View Code

    65 / 110 Problem G LightOJ 1245 Harmonic Number (II)

    先求出前sqrt(n)项和:即n/1+n/2+...+n/sqrt(n)

    再求出后面所以项之和.后面每一项的值小于sqrt(n),计算值为1到sqrt(n)的项的个数,乘以其项值即可快速得到答案

    例如:10/1+10/2+10/3+...+10/10

    sqrt(10) = 3

    先求出其前三项的和为10/1+10/2+10/3

    在求出值为1的项的个数为(10/1-10/2)个,分别是(10/10,10/9,10/8,10/7,10/6),值为2个项的个数(10/2-10/3)分别是(10/5,10/4),在求出值为3即sqrt(10)的项的个数.

    显然,值为sqrt(10)的项计算了2次,减去一次即可得到答案。当n/(int)sqrt(n) == (int)sqrt(n)时,值为sqrt(n)的值会被计算2次。

    #include <iostream>
    #include <cstdio>
    #include <cmath>
    using namespace std;
    
    int main()
    {
        int T,n;
        scanf("%d",&T);
        long long sum;
        for(int i = 1; i<=T; i++)
        {
            scanf("%d",&n);
            sum = 0;
            for(int j = 1; j<=sqrt(n); j++)
            {
                sum += n/j;
            }
            for(int j = 1; j<=sqrt(n); j++)
            {
                sum += (n/j - n/(j+1))*j;
            }
            if(n/(int)sqrt(n)==(int)sqrt(n))
                sum -= n/(int)sqrt(n);
            printf("Case %d: %lld
    ",i,sum);
        }
        return 0;
    }
    View Code

    48 / 172 Problem H LightOJ 1236 Pairs Forming LCM

    思路:把n分解成素因数的形式n=p1^c1+p2^c2+...pm^cm

    假设已找到一对(a,b)的lcm=n

    有a=p1^d1+p2^d2+...pm^dm

    b=p1^e1+p2^e2+...pm^em

    易知max(di,ei)=ci

    先考虑有序数对(a,b),由唯一分解定理知,a的每一个素因数的幂的大小都决定一个独一无二的数。

    所以(a,b)的种数就是(di,ei)的种数,即2*(ci+1)-1(因为有序对(c1,c1)重复了一次所以-1)

    所以有序对(a,b)的种数ans=(2*c1+1)*(2*c2+1)*(2*c3+1)*...*(2*cm+1)

    但是要求求无序对的种数,已知(a,b)(b,a)重复,除了(n,n)只算了一个之外,所以ans = ans/2 +1

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    
    typedef long long ll;
    const int N = 1e7+100;
    ll prime[N/10];
    bool vis[N];
    int tot;
    void ini()
    {
        for(ll i=2;i<N;i++)
            if(!vis[i])
            {
                prime[tot++]=i;
                for(ll j=i*i;j<N;j+=i) vis[j]=1;
            }
    }
    int main()
    {
        ini();
        int T;
        scanf("%d",&T);
        for(int cas=1;cas<=T;cas++)
        {
            ll n;
            cin>>n;
            ll t=n;
            ll ans=1;
            for(int i=0;i<tot&&prime[i]*prime[i]<=n;i++)
            {
                ll cnt=0;
                while(t%prime[i]==0) cnt++,t/=prime[i];
                ans*=(2*cnt+1);
            }
            if(t>1) ans*=3;
            ans = ans/2 +1;
            cout<<"Case "<<cas<<": "<<ans<<endl;
        }
        return 0;
    }
    View Code

    43 / 73 Problem I LightOJ 1234 Harmonic Number

    求1 + 1/2 + 1/3 + 1/4 + 1/ 5 +...+ 1/ n(1 ≤ n ≤ 108)

    这题是单调级数部分求和,网上有公式,不过不知道公式也是没关系的,毕竟这个知识点也偏门了点。。。

    我用的方法是打表记录1/i (1<=i<=n),根据题意,n最大为一亿,将一亿个结果记录下来肯定是不可行的,但是可以记录百万级个结果。下面的代码中,我开了一个250万的数组,0到一亿范围内,每40个数记录一个结果,即是分别记录1/40,1/80,1/120,...,1/一亿,这样对于输入的每个n,最多只需执行39次求倒数运算,大大节省了时间。

    注意的是,a[0] = 0,只是为了使得当n==1时不用单独判断。

    ps:当n很大时,有个近似公式:1+1/2+1/3+1/4+1/5+...+1/n=γ+ln(n) 
    γ是欧拉常数,γ=0.57721566490153286060651209...
    ln(n)是n的自然对数(即以e为底的对数,e=2.71828...)

    我用这个公式写了下,好像精度不太够。

    #include <iostream>
    #include <algorithm>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    
    using namespace std;
    
    const int maxn = 2500001;
    double a[maxn] = {0.0, 1.0};
    
    int main()
    {
        int t, n, ca = 1;
        double s = 1.0;
        for(int i = 2; i < 100000001; i++)
        {
            s += (1.0 / i);
            if(i % 40 == 0) a[i/40] = s;
        }
        scanf("%d", &t);
        while(t--)
        {
            scanf("%d", &n);
            int x = n / 40;
           // int y = n % 40;
            s = a[x];
            for(int i = 40 * x + 1; i <= n; i++) s += (1.0 / i);
            printf("Case %d: %.10lf
    ", ca++, s);
        }
        return 0;
    }
    View Code

    38 / 160 Problem J LightOJ 1220 Mysterious Bacteria

    题目大意:给你一个整数n(可能为负数),让你求满足a^p=n的最大的p
    思路:当n是正数时,直接对n进行素因子分解,在对它的素因子的个数进行gcd,比如12=2^2*3,gcd(2,1)就是最大的p;
    当n是负数时,则p的值一定是奇数,因为一个数的偶数次方一定为整数,因此需要将它的素因子个数全都化为奇数。

    #include<stdio.h>
    #include<string.h>
    const int MAXN=1000100;
    bool vis[MAXN];
    long long prime[MAXN/10];
    int tot=0;
    void getPrime()//求素数
    {
        for(long long i=2;i<MAXN;i++)
            if(!vis[i])
            {
                prime[tot++]=i;
                for(long long j=i*i;j<MAXN;j+=i) vis[j]=true;
            }
    }
    int a[1000];//保存素因子
    int b[1000];//保存素因子的个数
    int cnt;
    void sbreak(long long n){//进行素因子分解
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        cnt=0;
        for(int i=0;prime[i]*prime[i]<=n;i++){
            if(n%prime[i]==0){
                a[cnt]=prime[i];
                while(n%prime[i]==0){
                    b[cnt]++;
                    n/=prime[i];
                }
                cnt++;
            }
        }
        if(n!=1){
            a[cnt]=n;
            b[cnt++]=1;
        }
    }
    
    int gcd(int a,int b)  //求最大公约数
    {
        return b?gcd(b,a%b):a;
    }
    
    int main(){
        int T,ans,kase=0,flag;
        long long n;
        getPrime();
        scanf("%d",&T);
        while(T--){
            flag=1;//标志,判断n是正数还是负数
            scanf("%lld",&n);
            if(n<0) n=-n,flag=0;
            sbreak(n);
            int t=b[0];
            if(!flag){//如果n是奇数
                if(t%2==0){
                    while(t%2==0) t/=2;
                }
                for(int i=0;i<cnt;i++){//将它的素因子的个数化为奇数
                    if(b[i]%2==0){
                        while(b[i]%2==0) b[i]/=2;
                    }
                    t=gcd(t,b[i]);
                }
            }
            else for(int i=0;i<cnt;i++)  t=gcd(t,b[i]);
            printf("Case %d: %d
    ",++kase,t);
        }
        return 0;
    }
    View Code

    46 / 98 Problem K LightOJ 1214 Large Division

    d.问a能否被b整除

    s.大数

    c.借机水下java大数

    import java.math.BigInteger;
    import java.util.Scanner;
    
    public class Main {//类名要用Main
        public static void main(String[] args){
            
            int T;
            BigInteger a,b;
            BigInteger ZERO=new BigInteger("0");
            Scanner sc=new Scanner(System.in);
            int ca=0;
            
            T=sc.nextInt();
            while((T--)>0){
                
                a=sc.nextBigInteger();
                b=sc.nextBigInteger();
                System.out.print("Case ");
                System.out.print(++ca);
                if(a.remainder(b).compareTo(ZERO)==0){//b可能为负数,不能用mod函数
                    
                    System.out.println(": divisible");
                }
                else{//
                    System.out.println(": not divisible");
                }
                
            }
            
        }
    }
    View Code

    c2.大数模板,好臃肿啊!写起来也麻烦!用了下,还wa了好几发,很尴尬。%运算里面的d可能超int,得用long long才行,这种错不好找啊。。

    ps:能少用这个就少用,代码太长。

    /*
    整数大数
    int数组实现
    */
    #include<iostream>
    #include<stdio.h>
    #include<stdlib.h>
    #include<string.h>
    using namespace std;
    
    #define MAXN 9999//万进制
    #define DLEN 4//4位
    
    class BigNum{
    private:
        int a[500];//可以控制大数位数(500*4)
        int len;//大数长度
    public:
        BigNum(){//构造函数
            len=1;
            memset(a,0,sizeof(a));
        }
        BigNum(const int);//将int转化为大数
        BigNum(const char *);//将字符串转化为大数
        BigNum(const BigNum &);//拷贝构造函数
        BigNum &operator=(const BigNum &);//重载赋值运算符,大数之间赋值
    
        BigNum operator+(const BigNum &)const;//大数+大数
        BigNum operator-(const BigNum &)const;//大数-大数
        BigNum operator*(const BigNum &)const;//大数*大数
        BigNum operator/(const int &)const;//大数/int
    
        BigNum operator^(const int &)const;//幂运算
        int operator%(const int &)const;//取模
        bool operator>(const BigNum &)const;//大数与大数比较
        bool operator>(const int &)const;//大数与int比较
    
        void print();//输出大数
    };
    
    BigNum::BigNum(const int b){//将int转化为大数
        int c,d=b;
        len=0;
        memset(a,0,sizeof(a));
        while(d>MAXN){
            //c=d-(d/(MAXN+1))*(MAXN+1);
            c=d%(MAXN+1);//取出后四位
            d=d/(MAXN+1);//
            a[len++]=c;
        }
        a[len++]=d;
    }
    BigNum::BigNum(const char *s){//将字符串转化为大数
        int t,k,index,l,i,j;
        memset(a,0,sizeof(a));
        l=strlen(s);
        len=l/DLEN;
        if(l%DLEN)++len;
        index=0;
        for(i=l-1;i>=0;i-=DLEN){
            t=0;
            k=i-DLEN+1;
            if(k<0)k=0;
            for(j=k;j<=i;++j)
                t=t*10+s[j]-'0';
            a[index++]=t;
        }
    }
    BigNum::BigNum(const BigNum &T):len(T.len){//拷贝构造函数
        int i;
        memset(a,0,sizeof(a));
        for(i=0;i<len;++i)
            a[i]=T.a[i];
    }
    BigNum &BigNum::operator=(const BigNum &n){//重载复制运算符,大数之间赋值
        int i;
        len=n.len;
        memset(a,0,sizeof(a));
        for(i=0;i<len;++i)
            a[i]=n.a[i];
        return *this;
    }
    
    BigNum BigNum::operator+(const BigNum &T)const{//大数+大数
        BigNum t(*this);
        int i,big;//位数
        big=T.len>len?T.len:len;
        for(i=0;i<big;++i){
            t.a[i]+=T.a[i];
            if(t.a[i]>MAXN){
                ++t.a[i+1];
                t.a[i]-=MAXN+1;
            }
        }
        if(t.a[big]!=0)t.len=big+1;
        else t.len=big;
        return t;
    }
    BigNum BigNum::operator-(const BigNum &T)const{//大数-大数
        int i,j,big;
        bool flag;
        BigNum t1,t2;//t1大的,t2小的
        if(*this>T){
            t1=*this;
            t2=T;
            flag=0;//前面的大
        }
        else{
            t1=T;
            t2=*this;
            flag=1;//前面的小
        }
        big=t1.len;
        for(i=0;i<big;++i){
            if(t1.a[i]<t2.a[i]){
                j=i+1;
                while(t1.a[j]==0)++j;
                --t1.a[j--];
                while(j>i)t1.a[j--]+=MAXN;
                t1.a[i]+=MAXN+1-t2.a[i];
            }
            else t1.a[i]-=t2.a[i];
        }
        while(t1.a[t1.len-1]==0&&t1.len>1){
            --t1.len;
            --big;
        }
        if(flag)t1.a[big-1]=-t1.a[big-1];//前面的小,结果为负
        return t1;
    }
    BigNum BigNum::operator*(const BigNum &T)const{//大数*大数
        BigNum ret;
        int i,j,up;
        int temp,temp1;
        for(i=0;i<len;++i){
            up=0;
            for(j=0;j<T.len;++j){
                temp=a[i]*T.a[j]+ret.a[i+j]+up;
                if(temp>MAXN){
                    //temp1=temp-temp/(MAXN+1)*(MAXN+1);
                    temp1=temp%(MAXN+1);
                    up=temp/(MAXN+1);
                    ret.a[i+j]=temp1;
                }
                else{
                    up=0;
                    ret.a[i+j]=temp;
                }
            }
            if(up!=0)ret.a[i+j]=up;
        }
        ret.len=i+j;
        while(ret.a[ret.len-1]==0&&ret.len>1)--ret.len;
        return ret;
    }
    BigNum BigNum::operator/(const int &b)const{//大数/int
        BigNum ret;
        int i,down=0;
        for(i=len-1;i>=0;--i){
            ret.a[i]=(a[i]+down*(MAXN+1))/b;
            down=a[i]+down*(MAXN+1)-ret.a[i]*b;
        }
        ret.len=len;
        while(ret.a[ret.len-1]==0&&ret.len>1)--ret.len;
        return ret;
    }
    
    BigNum BigNum::operator^(const int &n)const{//幂运算
        BigNum t,ret(1);
        int i;
        if(n<0)exit(-1);
        if(n==0)return 1;
        if(n==1)return *this;
        int m=n;
        while(m>1){
            t=*this;
            for(i=1;i<<1<=m;i<<=1){
                t=t*t;
            }
            m-=i;
            ret=ret*t;
            if(m==1)ret=ret*(*this);
        }
        return ret;
    }
    int BigNum::operator%(const int &b)const{//取模
        int i;
        long long d=0;//这个题这里必须用long long,还得。。可能会超限。
        for(i=len-1;i>=0;--i){
            d=((d*(MAXN+1))%b+a[i])%b;
        }
        return d;
    }
    bool BigNum::operator>(const BigNum &T)const{//大数与大数比较
        int ln;
        if(len>T.len)return true;
        else if(len==T.len){
            ln=len-1;
            while(a[ln]==T.a[ln]&&ln>=0)--ln;
            if(ln>=0&&a[ln]>T.a[ln])return true;
            else return false;
        }
        else return false;
    }
    bool BigNum::operator>(const int &t)const{//大数与int比较
        BigNum b(t);
        return *this>b;
    }
    
    void BigNum::print(){//输出大数
        int i;
        printf("%d",a[len-1]);
        for(i=len-2;i>=0;--i){
            printf("%.4d",a[i]);//%.4d代表4位,不够前面补0
        }
        printf("
    ");
    }
    
    int main(){
        /*
        char str1[]="2",str2[]="22222222222222222222222222222222222222222222";
        int c=2;
        //scanf("%s%s",str1,str2);
        BigNum a,b,t;
        a=BigNum(str1);
        b=BigNum(str2);
        printf("a=");a.print();
        printf("b=");b.print();
        printf("c=%d
    ",c);
        printf("
    ");
    
        t=a+b;
        printf("a+b=");t.print();
        t=a-b;
        printf("a-b=");t.print();
        t=a*b;
        printf("a*b=");t.print();
        t=a/c;
        printf("a/c=");t.print();
        printf("
    ");
    
        t=a^c;
        printf("a^c=");t.print();
        t=a%c;
        printf("a%%c=");t.print();
    
        a>b?printf("a>b
    "):printf("a<=b
    ");
        a>c?printf("a>c
    "):printf("a<=c
    ");
        */
        int T;
        BigNum a;
        int b;
        char str1[300];
        int t;
        int ca=0;
    
        scanf("%d",&T);
    
        while(T--){
            scanf("%s%d",str1,&b);
            if(str1[0]=='-'){
                a=BigNum(str1+1);
            }
            else{
                a=BigNum(str1);
            }
            if(b<0){
                b=-b;
            }
    
            t=a%b;
            if(t==0){
                printf("Case %d: divisible
    ",++ca);
            }
            else{
                printf("Case %d: not divisible
    ",++ca);
            }
        }
    
        return 0;
    }
    View Code

    c3.直接写也挺好啊~网上代码。里面的res也得用long long。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
     
    using namespace std;
     
    char str[205];
    long long mod;
     
    int main()
    {
        int t;
        scanf("%d",&t);
        int cas = 0;
        while( t-- )
        {
            scanf("%s %lld",str,&mod);
            //printf("%s %d
    ",str,mod);
            int len = strlen( str );
            int s = 0;
            if( str[0] == '-' )
                s++;
            long long res = 0;
            for( int i = s; i < len; i++ )
                {
                    res = res*10 + str[i] - '0';
                    res = res % mod;
                }
            if( !res )
                printf("Case %d: divisible
    ",++cas);
            else
                printf("Case %d: not divisible
    ",++cas);
        }
        return 0;
    }
    View Code

    35 / 62 Problem L LightOJ 1213 Fantasy of a Summation

    题意:给你一个求和的循环,让你计算最后的结果是多少?

    思路:首先,我们得明确在这个循环里面,a[0]……a[n-1]出现的次数是一样多的,
    它大的累加的次数为n^k次方。
    看第二个样例:
    它有2个数字,3层循环
    1、a[0]   a[0]   a[0]
    2、a[0]   a[0]   a[1]
    3、a[0]   a[1]   a[0]
    4、a[0]   a[1]   a[1]
    5、a[1]   a[0]   a[0]
    6、a[1]   a[0]   a[1]
    7、a[1]   a[1]   a[0]
    8、a[1]   a[1]   a[1]
    在这里面它的大的累加次数为2^3=8次,因为每个数的累加次数一样多的
    它每个数的累加次数为(n^k)*k/n=(n^(k-1))*k,
    则和就等于(sum(a[i])*(n^(k-1))*k)%mod

    #include<stdio.h>
    long long pow(int a,int n,int mod){
        if(n==0) return 1;
        long long t=pow(a,n/2,mod);
        t=t%mod;
        long long ans=t*t%mod;
        if(n%2==1) ans=(ans*(a%mod))%mod;
        return ans;
    }
    int main(){
        int T,kase=0;
        int a[1005];
        scanf("%d",&T);
        while(T--){
            int n,k,mod;
            long long ans=0;
            scanf("%d%d%d",&n,&k,&mod);
            for(int i=0;i<n;i++){
                scanf("%d",&a[i]);
                ans=(ans+a[i]%mod)%mod;
            }
            long long t=pow(n,k-1,mod);
            ans=((ans*(k%mod)%mod)*t)%mod;
            printf("Case %d: %lld
    ",++kase,ans);
        }
        return 0;
    }
    View Code

    41 / 137 Problem M LightOJ 1197 Help Hanzo

    给你a和b求a到b之间的素数个数。

    先在小区间素数筛,大区间就用类似素数筛的想法,把a到b之间不是素数的标记出来。因为b-a最多1e5的大小,所以每组数据的时间复杂度最多就o(1e5 log1e5)。

    ps:我感觉这个复杂度为O(1e5+log1e5)

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    const int MAXN = 2e5 + 5;
    typedef long long LL;
    bool prime[MAXN] , vis[MAXN];
    LL p[MAXN / 5];
    
    void init() {
        prime[1] = true;
        int cont = 0;
        for(int i = 2 ; i < MAXN ; i++) {
            if(!prime[i]) {
                p[++cont] = i;
                for(int j = i * 2 ; j < MAXN ; j += i) 
                    prime[j] = true;
            }
        }
    }
    
    int main()
    {
        init();
        int t , a , b;
        scanf("%d" , &t);
        for(int ca = 1 ; ca <= t ; ca++) {
            scanf("%d %d" , &a , &b);
            int res = 0;
            if(b < MAXN) {
                for(int i = a ; i <= b ; i++) {
                    if(!prime[i])
                        res++;
                }
            }
            else {
                memset(vis , false , sizeof(vis));
                for(int i = 1 ; p[i]*p[i] <= b ; i++) {
                    LL k = a / p[i];
                    if(k*p[i] < a)
                        k++;
                    if(k == 1) //此时a%p[i]==0 && a/p[i]==1,说明a刚好是一个素数
                        k++;
                    while(k * p[i] <= b) { //筛选a~b中不是素数的
                        vis[k*p[i] - a] = true;
                        k++;
                    }
                }
                for(int i = a ; i <= b ; i++) {
                    if(!vis[i - a])
                        res++;
                }
            }
            printf("Case %d: %d
    " , ca , res);
        }    
    }
    View Code

    45 / 100 Problem N LightOJ 1138 Trailing Zeroes (III)

    d.给一个Q,代表N!末尾有Q个0。给出Q,找到最小的N。

    s.可以用二分法,找到最小的N。

    然后看看怎么求N!后面有几个0?

    有算数基本定理可知 N!可划分为质因数相乘的形式  n!=2x*3y*5z*...

    因为只有2*5才会出现0,又因为2的个数比5多(可见下面证明),所以N!后面的0的个数就是N!的分解中的5的个数。

    那么如何求一个数的阶乘的结果5的幂次方是多少。

    譬如30!的结果中,5的幂究竟是多少,答案是30/5 = 6, 6/5 = 1;结果为7个。除到最后的数<5为止。可以自己在纸上验证。

    证明:2的个数比5多

    对n!做质因数分解n!=2x*3y*5z*...

    显然0的个数等于min(x,z),并且min(x,z)==z

    对于阶乘而言,也就是1*2*3*...*n
    [n/k]代表1~n中能被k整除的个数
    那么很显然
    [n/2] > [n/5] (左边是逢2增1,右边是逢5增1)
    [n/2^2] > [n/5^2](左边是逢4增1,右边是逢25增1)
    ……
    [n/2^p] > [n/5^p](左边是逢2^p增1,右边是逢5^p增1)
    随着幂次p的上升,出现2^p的概率会远大于出现5^p的概率。
    因此左边的加和一定大于右边的加和,也就是n!质因数分解中,2的次幂一定大于5的次幂。

    #include <stdio.h>
    #include <math.h>
    #include <vector>
    #include <queue>
    #include <string>
    #include <string.h>
    #include <stdlib.h>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    typedef long long LL;
    
    LL solve(LL n)
    {
        LL num=0;
        while(n)
        {
            num+=n/5;
            n/=5;
        }
        return num;
    }
    
    LL er(LL n)
    {
        LL x=1;
        LL y=0x3f3f3f3f;
        LL mid;
        LL res=-1;
        while(x<=y)
        {
            mid=(x+y)/2;
            LL ans=solve(mid);
            if(ans==n)
            {
                res=mid;
                y=mid-1;
                //return mid;
            }
            else if(ans>n)
            {
                y=mid-1;
            }
            else if(ans<n)
            {
                x=mid+1;
            }
        }
        return res;
    }
    int main()
    {
    
        int t;
        scanf("%d",&t);
        int xp=1;
        while(t--)
        {
            LL n;
            scanf("%lld",&n);
            LL ans=er(n);
            if(ans==-1)  printf("Case %d: impossible
    ",xp++);
            else printf("Case %d: %lld
    ",xp++,ans);
        }
        return 0;
    }
    View Code

    37 / 48 Problem O UVA 11426 GCD - Extreme (II)

    d.

    G=0;
    for(i=1;i<N;i++)
    for(j=i+1;j<=N;j++)
    {
    G+=gcd(i,j);
    }

    求G。

    s.假设a、b(a<b)互质,那么gcd(a,b)=1,这样当i循环到a、j循环到b时就会向结果中+1,而i循环到2*a、j循环到2*b时就会向结果中+2(gcd(2*a,2*b)=2)...循环到k*a和k*b时就会向结果中+k。这样实际上引起结果变化的根源就在于各对互质的数,当i、j循环到他们自身或者自身的倍数时就会引起结果的改变,那么我们不妨先将每对互质的数对结果的贡献值算出来,最后将各对互质的数对结果的贡献累加起来就可以了。

    假设和b互质的数有n个,也就是n对(?,b)(?和b互质),那么在i、j循环到?、b时结果会增加n,循环到(2*?,2*b)时结果就会增加2*n...当i、j循环到k*?、k*b时结果就会增加k*n。那么我们不妨用a[i]记录各种k、b在满足k*b=i时会增加多少结果,a[i]代表gcd(x,i)的和,其中x小于i,那么最后我们要输出的就是a[2]+a[3]+...+a[N]。

    至于找和b互质的数,就是计算b的欧拉函数的值,然后暴力循环k,并修改对应的a[k*b]即可,整体的复杂度是O(N*logN)的。

    欧拉函数扩展: 欧拉公式的延伸:小于n 与n互质的数的和 是euler(n)*n/2

    //欧拉函数复杂度O(nlogn)
    #include<stdio.h>
    #include<string.h>
    #include <iostream>
    using namespace std;
    #define MAXD 4000010
    const int N = 4000000;
    typedef long long LL;
    int phi[MAXD];
    LL a[MAXD];
    
    void prep()
    {
        memset(a, 0, sizeof(a));
        for(int i = 1; i <= N; i ++)
            phi[i] = i;
        for(int i = 2; i <= N; i ++)
        {
            if(phi[i] == i){ ///质数
                for(int j = i; j <= N; j += i){
                    phi[j] = phi[j] / i * (i - 1);
                }
            }
            for(int j = 1; j * i <= N; j ++)  
                a[j * i] += j * phi[i]; ///通过对i*j的质因子来求
        }
        for(int i = 1; i <= N; i ++)
            a[i] += a[i - 1];
    }
    
    int main()
    {
        prep();
        int n;
        while(scanf("%d", &n), n)
            printf("%lld
    ", a[n]);
        return 0;
    }
    View Code

    13 / 65 Problem P UVA 11754 Code Feat
    9 / 26 Problem Q UVA 11916 Emoogle Grid
    36 / 86 Problem R POJ 1061 青蛙的约会

    s.设A在后,B在前,当A与B相遇时,满足:(m-n)*X=(y-x)+L*Y;X是时间,Y是圈数。

    移项:(m-n)*X-L*Y=(y-x);

    令a=m-n,b=-L,d=gcd(a,b)。则(y-x)%d==0时有解,否则无解。

    求出特解,再求最小的X即可。

    /*
    扩展欧几里德算法(求ax+by=gcd的解以及逆元)
    */
    #include<iostream>
    #include<stdio.h>
    #include<stdlib.h>
    using namespace std;
    
    //返回d=gcd(a,b);和对应于等式ax+by=d中的x,y
    long long extend_gcd(long long a,long long b,long long &x,long long &y){
        if(a==0&&b==0)return -1;//无最大公约数
        if(b==0){x=1;y=0;return a;}
        long long d=extend_gcd(b,a%b,y,x);
        y-=a/b*x;
        return d;
    }
    
    int main(){
    
        int x,y,m,n,L;
        long long a,b,x0,y0,d;
        long long abs_bd;
    
        while(~scanf("%d%d%d%d%d",&x,&y,&m,&n,&L)){
            a=m-n;
            b=-L;
            d=extend_gcd(a,b,x0,y0);
    
            abs_bd=abs(b/d);
    
            if((y-x)%d==0){
                x0=(x0*((y-x)/d))%b;
                //x0=(x0%(b/d)+(b/d))%(b/d);
                x0=(x0%abs_bd+abs_bd)%abs_bd;
                printf("%lld
    ",x0);
            }
            else{
                printf("Impossible
    ");
            }
        }
    
        return 0;
    }
    View Code

    24 / 63 Problem S POJ 2115 C Looooops

    hint:见之前博客


    13 / 35 Problem T POJ 2116 Death to Binary?
    61 / 122 Problem U HDU 2161 Primes

    输入n,是素数输出yes,不是则输出no。

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    using namespace std;
    
    int p[16001];
    
    int main()
    {
        int n,k=0;
        memset(p,-1,sizeof(p));
        p[1]=0;
        p[2]=0;
        for(int i=3; i<=16000; i++)
        {
            for(int j=2; j<=sqrt(i); j++)
            {
                if(i%j==0)
                {
                    p[i]=0;
                    break;
                }
            }
        }
    
        while(~scanf("%d",&n))
        {
            if(n<=0)
                return 0;
            if(p[n]==-1)
                printf("%d: yes
    ",++k);
            else
                printf("%d: no
    ",++k);
        }
    
        return 0;
    }
    View Code

    32 / 87 Problem V UVA 11827 Maximum GCD

    题意:给你一组数,求出其中两两最大公约数中最大的值。

    思路:数据较小,直接枚举。

    #include<stdio.h>
    int gcd(int a,int b){//求最大公约数
        return b?gcd(b,a%b):a;
    }
    int main(){
        int T;
        int a[105];
        char c;
        scanf("%d",&T);
        while (getchar() != '
    ');
        while(T--){
            int cnt=0;
            while((c=getchar())!='
    '){
                if(c>='0' && c<='9'){
                    ungetc(c,stdin);//将字符c退回到输入流中
                    scanf("%d",&a[cnt++]);
                }
            }
            int max=0;
            for(int i=0;i<cnt-1;i++){
                for(int j=i+1;j<cnt;j++){
                    int t=gcd(a[i],a[j]);
                    if(t>max) max=t;
                }
            }
            printf("%d
    ",max);
        }
        return 0;
    }
    View Code

    25 / 98 Problem W UVA 10200 Prime Time

    题意:通过公式计算出一个数,判断这个数是否为素数。在区间[a,b]上通过公式算出素数占总数的百分比。

    ps:里面加上1e-8是什么梗

    #include <stdio.h>
    #include <string.h>
    int prime(int n)
    {
        int i;
        for(i=2;i*i<=n;i++)
        {
            if((n%i)==0)
                return 0;
        }
        return 1;
    }
    int main()
    {
        int num[10010];
        int i;
        int a,b;
        int sum;
        memset(num,0,sizeof(num));
        for(i=0;i<=10005;i++)
            num[i]=prime(i*i+i+41);
        while(scanf("%d%d",&a,&b)!=-1)
        {
            sum=0;
            for(i=a;i<=b;i++)
                sum+=num[i];
            printf("%.2f
    ",sum*1.0/(b-a+1)*100+1e-8);//精度
        }
        return 0;
    }
    View Code

    20 / 154 Problem X SGU 106 The equation
    34 / 51 Problem Y POJ 2478 Farey Sequence

    题意:给定一个数n,求小于或等于n的数中两两互质组成的真分数的个数。 表达的有点挫,很直接的欧拉函数的应用。

    phi(x) 表示与x互质且小于x的正整数的个数。

    #include<iostream>
    #include<cstdio>
    #include<cmath>
    #include<cstring>
    #include<algorithm>
    #define see(x) cout<<#x<<":"<<x<<endl;
    #define MAXN 1000005
    using namespace std;
    
    int prime[MAXN],phi[MAXN];
    bool notp[MAXN];
    void Prime(){
        int i, j, np=0;
        phi[1] = 0;
        memset(notp,false,sizeof(notp));
        for(i=2;i<MAXN;i++){
            if(notp[i]==false){
                prime[np++] = i;
                phi[i] = i-1; 
            }
            for(j=0;j<np&&i*prime[j]<MAXN;j++){
                notp[i*prime[j]] = true;
                if(i%prime[j]==0){
                    phi[i*prime[j]] = phi[i]*prime[j];
                    break;
                }
                else{
                    phi[i*prime[j]] = phi[i]*(prime[j]-1);
                }
            }
        }
    }
    
    long long ans[MAXN];
    int main(){
        int n, i;
        Prime();
        ans[1] = 0;
        for(i=2;i<MAXN;i++){
            ans[i] = ans[i-1] + phi[i];
        }
        while(scanf("%d",&n)&&n){
            cout<<ans[n]<<endl;
        }
        return 0;
    }
    View Code

    这里要说到关于筛素数的方法。以下算法复杂度为O(n)

    void Prime(){
        int i, j, np=0;
        memset(notp,false,sizeof(notp));
        for(i=2;i<MAXN;i++){
            if(notp[i]==false){
                prime[np++] = i;
            }
            for(j=0;j<np&&i*prime[j]<MAXN;j++){
                notp[i*prime[j]] = true;
                if(i%prime[j]==0){   //O(n)的关键
                    break;
                }
            }
        }
    }
    View Code

    适时跳出,大大降低了算法复杂度。本质就是任意一个合数都有一个最小质因数,最终被赋值为true时,都是由其的最小质因数筛出来的,其被筛出来后也无需再关心其他数了。

    原因如下:设 i 的最小质因数为p[j],则 i%p[j]==0,设k=i/p[j],w=i*p[j]=k*p[j]*p[j],w是由k*p[j]和p[j]共同筛出来的,对于比w更大的也有因子p[j]的数ww,则自然可以由后面的m*p[j](m>k)和p[j]共同筛出来了。

    对筛素数的代码,略加几行代码,就可以顺带求出欧拉函数phi[x]了。用到了递推式:

    对于p|x。若p2|x,则phi(x) = phi(x/p)*p;否则phi(x) = phi(x/p)*(p-1)


    23 / 63 Problem Z UVA 11752 The Super Powers

    题目大意:没有输入,找出所有的超级数,超级数即可以拆分成至少两个数的幂形式。

    解题思路:先用素数筛选法找出64以内的合数,以为只有幂是合数才可以进行拆分。然后枚举底数进行判断,所有小于2^64-1的数都是满足的,这里有一个技巧,就是说2^64-1已经是unsign long long 的最大值了,那么超过它的数就会变成负数。但其实i的最大次幂是可以求的,x = logi(2^64-1) = log(2^64-1) / log(i);

    #include <stdio.h>
    #include <string.h>
    #include <math.h>
    #include <set>
    #include <algorithm>
    
    using namespace std;
    typedef unsigned long long ll;
    const int N = 105;
    int g, v[N], a[N];
    
    void init () {
        g = 0;
        memset(v, 0, sizeof(v));
    
        for (int i = 2; i <= 64; i++) {
            if (v[i]) {
                a[g++] = i;
                continue;
            }
    
            for (int j = i * 2; j <= 64; j += i) v[j] = 1;
        }
    }
    
    void solve () {
        set<ll> ans;
        ans.insert(1);
    
        ll MaxT = 1<<16;
        for (ll i = 2; i < MaxT; i++) {
            int ti = ceil(64 * log(2) / log(i)) - 1;
            ll now = i * i * i * i;
            ans.insert(now);
            for (int j = 1; a[j] <= ti; j++) {
                now *= (a[j] - a[j-1] == 1 ? i : i * i);
                ans.insert (now);
            }
        }
    
        for (set<ll>::iterator i = ans.begin(); i != ans.end(); i++)
            printf("%llu
    ", *i);
    }
    
    int main () {
        init();
        solve ();
        return 0;
    }
    View Code
  • 相关阅读:
    线程之同步函数
    selenium破解极验验证
    数据结构--树
    数据结构--线性表
    数据结构--栈
    数据结构--队列
    代理的初步理解
    javamail+ical4j发送会议提醒
    阿里云学习之IOT物联网套件(客户端与服务端的后台数据传输)
    位图数据结构原理分析总结
  • 原文地址:https://www.cnblogs.com/gongpixin/p/5360676.html
Copyright © 2011-2022 走看看