zoukankan      html  css  js  c++  java
  • KeepCode 3 解题报告

    题目来源

    IDOriginTitle
    Problem A HDU 4407 Sum
    Problem B POJ 1845 Sumdiv
    Problem C POJ 2480 Longge's problem
    Problem D POJ 1012 Joseph
    Problem E POJ 1082 Calendar Game
    Problem F POJ 1099 Square Ice

    Problem A 

      将题目转换下, 我们 定义函数 Sum ( 1, N ) 为 区间[ 1, N ] 与 P 互质的数的和

      则 Sum( 1, Y ) - Sum( 1, X-1) 即为 区间 [ X, Y ] 与 P 互质的数的和

      再回到本题 

        N = 400000 , M = 1000

      题目中仅有两种操作, 1为统计区间与P互质数的和, 2为更改 X 位置值 为C

      如果我们只进行 1 操作, 则我们通过定义的 Sum 函数可以很轻松的解决这个问题.

      因为 操作数目 M = 1000, 相对于 N 来讲, 它是很小的, 所以我们可以考虑将 操作2 忽视,( 单独来处理对应位置的变化情况 )

      这样我们就可以简化问题, 通过 Sum 函数来解决这个问题. Sum函数的计算方法如下:

    问题: 求区间 [ 1, N ] 与 P 互质的数 的和

      首先设:  A 为 区间[1,N]与P 互质的数 的和 

          B 为 区间[1,N] 所有数的和 ( 等差数列求和 B =  (1+N)*N/2 )

          C 为 区间[1,N]与P 不互质的数 的和

      那么我们知道:

        A = B(全集) - C( A的补集 )

      明显集合 A 不好求, 我们考虑从侧面来计算.  全

      集 B 等差数列求解没问题, 至于 A的补集 C 我们可以通过 容斥原理 来求解:

    首先来看容斥原理的表达式:  

        ( 核心操作: 加奇减偶 )

          

      任意正整数都可以因式分解为如下形式:

               其中( p1, p2 ... pk 为质数, ei 为次数 )

      那么任意 X , 与N 不互质, 则  GCD( N, X ) > 1

      意味着它们有公共的素因子 (最大公约数为非素数,其也是由素数相乘构成 )

      定义 F( P ) 为区间 [1, N] 以 P为因子( 不仅仅是质因子) 的数的总和

      则

       

      对于 N = 400000 以内所有数,因式分解后, 最多 不同的素因子不超过10个. 我们可以通过二进制状态枚举素因子组合情况来求 F(X)

      注意: 

        对 P 进行因式分解时, 我们可以通过试除法, 仅需筛选到    就可以了, 因为之后哪怕有素因子,也只有一个. 不可能出现平方甚至更多次方.

    否则会 TLE.

    解题代码:

    View Code
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    #include<map>
    #include<set>
    #include<algorithm>
    using namespace std;
    
    typedef long long LL;
    // 此出用来处理变换的操作
    // map用来映射,得到最新的更新情况
    set < int > S;
    map < int,int > Mp;
    
    const int N = 1010;
    const int maxn = 1010;
    
    int n, m;
    int prime[maxn],size; //maxn以内素数数量
    bool vis[maxn];
    
    void GetPrime()
    {    //线性筛选素数
        // 素数筛选只处理到 sqrt(400000) 能大幅度降低处理时间,但分解素因子的时候注意 大于sqrt(400000)素因子的情况 
        memset( vis, 0, sizeof(vis));    
        size = 0; vis[0] = vis[1] = true;
        for(int i = 2; i < maxn; i++)
        {
            if( !vis[i] ) prime[size++] = i;
            for(int j = 0; j < size && prime[j]*i < maxn; j++)
            {
                vis[ prime[j]*i ] = true;
                if( i%prime[j] == 0 ) break;
            }
        }
        //素数数量
        //printf("size = %d\n", size);
    }   
    inline LL Include( int y, int p ) //使用容斥原理,求 [1,y] 区间与 p 不互素的和
    {    
        int num = 0, a[10], t = p;    
        for(int i = 0; i < size && prime[i] <= t; i++)    
        {
            if( t%prime[i] == 0 )
            {
                a[num++] = prime[i];
                while(t%prime[i] == 0) t/=prime[i]; // 因为这里少写了个0,WA了一次。
            }
            if( t == 1 ) break;
        }    
        if( t > 1 ) a[num++] = t; //分解素因子的时候注意 大于sqrt(400000)素因子的情况的处理     
        //注意,把400000以内所有数字预处理会TLE,题目最多1000此op=1,所以每次求一次还快点 
    
        LL res = 0; // 存储结果,注意数值溢出    
        int mask = (1<<num)-1; //二进制表示对应位置取或不取,枚举组合情况
        for(int i = 1; i <= mask; i++)
        {
            int tot = 0;
            LL d = 1;    
            for(int j = 0; j < num; j++)
            {
                if( i&(1<<j) ) 
                {    tot++; d = d*a[j]; }
            }
            // 等差数列求和计算当前素数组合在区间[1,y]内的倍数的和    
            // 区间[1,y]为d的倍数,一共有y/d个,形成一个差值为d的等差数列        
            LL nn = 1LL*y/d, a1 = d, an = a1 + 1LL*(nn-1)*d;
            LL tmp = (a1+an)*nn/2;        // 等比数列求和
            if( (tot&1) == 0 ) tmp = -tmp; //容斥 加奇减偶
            res += tmp; 
        }
        return res;
    }
    LL solve( int y, int p ) //计算区间[1,y]内与p互质的数的和
    {
        // 互质和sum = [1,y]区间所有数和 - 与p不互质的和
        LL sum = 1LL*(1+y)*y/2 - Include( y, p );
        return sum;
    }
    
    int gcd( int a, int b )
    {    return (b==0)? a : gcd(b,a%b); }
    
    int main()
    {
    //    init();
        GetPrime();
        int T;
        scanf("%d", &T);
        while( T-- )
        {
            scanf("%d%d", &n,&m);
            Mp.clear();
            S.clear();
            int op, x, y, p;
            while( m-- )
            {
                scanf("%d", &op);
                if( op == 1 )
                {
                    scanf("%d%d%d", &x, &y, &p);
                    LL res = solve(y,p) - solve(x-1,p);
                    // 再处理区间[x,y]发生变换的    
                    for(set<int>::iterator it = S.begin(); it != S.end(); it++ )
                    {
                        if( (*it >= x) && (*it <= y) )    
                        {
                            if( gcd( p, *it ) == 1 )  res -= *it; //若s[i]与p互质则需要减去该值    
                            int t = Mp[ *it ];
                            if( gcd(t,p) == 1 ) res += t; //若 t 与p 互质则需加该值
                        }    
                    }
                    printf("%lld\n", res);
                }
                else{
                    scanf("%d%d", &x, &p );
                    S.insert(x);
                    Mp[x] = p;
                }    
            }
        }
        return 0;
    }

    Problem B

      任意正整数都可以因式分解为如下形式:

               其中( p1, p2 ... pk 为质数, ei 为次数 )

      定义函数 F( N ) 为 N 的因子和

      则  

      对于    

      因为 pi 为质因子, 两两互斥, ( 积性函数性质     (x,y)两两互斥 ), 所以

       

      

      经过以上分析, 对于本题, 可以得到

      

      

      

      所以我们可以通过将 A 进行因式分解后, 对素因子单独计算然后相乘 就可以了

      提示:

        1. 对于幂的计算, 使用二分快速幂即可

        2. 对于等比数列求和时, 计算   时, 对于 除以 ( p-1 ), 我们可以通过求 (p-1) 逆元来避免除法.

    但是要注意 不可求逆元 的特殊情况:

          p mod 9901 = 0  时,  

          p mod 9901 = 1  时,  

        3. 素数筛选的时候, 我们可以只筛选到    即可, 用试除法分解其素因子.

    解题代码

    View Code
    #include<stdio.h>
    #include<math.h>
    #include<string.h>
    #include<stdlib.h>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    
    typedef long long LL;
    const int N = 10010; //注意,为降低时间,我们筛选到sqrt(50000000)就可以了
    const int mod = 9901;
    
    int p[2000], size;
    bool vis[N];
    void GetPrime()
    {
        memset( vis, 0, sizeof(vis));
        size = 0;
        for(int i = 2; i < N; i++)
        {
            if( !vis[i] ) p[size++] = i;
            for(int j = 0; (j<size)&&(p[j]*i<N); j++)
            {
                vis[ p[j]*i ] = true;
                if( i%p[j] == 0 ) break;
            }
        }
    //    printf("size = %d\n", size);
    }
    LL Mul( LL a, LL b )
    {//按位模拟乘法,避免溢出
        LL res = 0;
        while( b )
        {
            if( b&1 ) if( (res+=a) >= mod ) res -= mod;
            a <<= 1; if( a >= mod ) a -= mod;
            b >>= 1;
        }
        return res;
    }
    LL Pow( LL x, LL k )
    { //按位模拟快速幂
        if( k ==  0 ) return 1;
        LL res = Pow( x, k/2 )%mod;
        res = res*res%mod;
        if( k&1 ) res *= x;
        return res;
    }
    LL ExGcd( LL a, LL b, LL &x, LL &y)
    {
        if(b == 0) { x = 1; y = 0; return a; } 
        LL r = ExGcd( b, a%b, x, y );
        LL t = x; x = y; y = t-a/b*y;
        return r;
    }
    LL Inverse( LL A )
    {//求逆元
        LL x, y;
        ExGcd( A, mod, x , y );
        return ((x%mod)+mod)%mod;
    }
    
    LL Sum( LL q, LL n )
    {//等比数列求和 , 注意特殊情形,不能求逆元
        if( q%mod == 0 ) return 1;
        else if( q%mod == 1 ) return n%mod;
        else
        {
            LL A = (Pow(q%mod,n) - 1 + mod)%mod, B = Inverse( q-1 );     
            LL res = A*B%mod; 
            return res;
        }
    }    
    
    LL solve( int A, int B )
    {
        int num = 0, a[50], b[50], t = A;
        for(int i = 0; (i < size) && (p[i] <= t) ; i++)
        {
            if( t%p[i] == 0 )
            {
                a[num] = p[i];
                b[num] = 1;
                t /= p[i];    
                while( t%p[i] == 0 )
                {
                    t /= p[i];
                    b[num]++;
                }
                num++;    
            }
            if( t == 1 ) break;    
        }
        if( t > 1 ) { a[num] = t; b[num]=1; num++; }
     
        LL ans = 1;
        for(int i = 0; i < num; i++)
            ans = ans*Sum( a[i], 1LL*b[i]*B+1 )%mod;//加上p^0项,共b[i]*B+1项    
        return ans;
    }
    int main()
    {
        GetPrime();
        int A, B;
        while( scanf("%d%d", &A, &B) != EOF)
        {
            if( (A == 0) || (A == 1) )
                printf("%d\n", A );    
            else
            {
                LL ans = solve( A, B );    
                printf("%lld\n", ans );    
            }    
        }
        return 0;
    }

    Problem C

      欧拉函数      表示 区间[1, N]  与N互质的数量个数

      对于 区间[ 1, N ]  其中的任意数 X, 如果有

            GCD( N, X ) = K

      则等价于  GCD(   ,   ) = 1 

      等价于 E(  )  在区间 [ 1,  ] 与  互质的数量个数

      对于本题, 枚举 N 因子, 仅需枚举 到   , 注意当 * = N 时的特殊情况处理

    解题代码

    View Code
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    #include<math.h>
    using namespace std;
    
    typedef long long LL;
    LL eular( LL x )
    {
        if( x == 0 ) return 0;
        LL res = 1, t = x;
        for(int i = 2; i <= (int)sqrt(1.*x); i++)
        {
            if( t%i == 0 )
            {
                res *= (i-1);
                t /= i;
                while( t%i == 0 ){ res *= i; t /= i; }
            }
            if(t == 1) break;    
        }
        if(t > 1) res *= (t-1);
        return res;
    }
    
    int main()
    {
        int n;
        while( scanf("%d", &n ) != EOF)
        {
            LL res = eular(n) + n;
            for(int i = 2; i <= (int)sqrt(n); i++)
            {
                if( n%i == 0 )
                {
                    if( i*i == n ) res += eular(i)*i;
                    else
                    {
                        res += eular( i )* (n/i);
                        res += eular( n/i ) * i;
                    }    
                }
            }
            printf("%lld\n", res );    
        }
        return 0;
    }

    Problem D 

      2K个人围成一圈,分别编号为 1,2,3,...,k,k+1,...,k+k,  前 k 次需要首先 Excute 编号为[ k+1, k+k ] 的 k 个人

        看到题目后一直想用 模线性同余方程组 搞, 后面弄好了好久,发现没规律可言, 然而K又不大,只接模拟暴力出结果然后打表就OK了.

      每次 Excute 后减少一人. 

      现在假设 数值为M ,则 当 k = 6 时, 因为每次的起点s是不同的,我们假定为 第i次的地点为 si

        第一次,  s1 + M % 2k

        第二次,  s2 + M % (2k-1)

        ....

        第 k 次,  sk  + M % (k+1)

      当然, si 是对 2K 取模, 因为围成了一个圈.  枚举 M 后, 模拟求出 K 次 ExCute的情况, 然后判定是否 合理.

    解题代码

    View Code
    #include<stdio.h>
    #include<string.h>
    #include<stdlib.h>
    /*
    // 计算结果代码
    int main()
    {
        int k = 6;
        bool vis[100];
        //while( scanf("%d",&k) != EOF )
        for( k = 1; k <= 13; k++)    
        {
            for(int m = 1; ; m++)
            {
                int s = 0, C = k+k;
                memset( vis , 0, sizeof(vis) );    
                for(int i = k+k; i > k; i-- )
                {
                    int r = m%i, cnt = 1;
                    while( (cnt%i) != r )
                    {
                        s = (s+1)%C;    
                        if( !vis[s] ) cnt++;    
                    }
                    vis[s] = true;    
                    s = (s+1)%C;
                    while( vis[s] ) s = (s+1)%C;
                }
                bool flag= true;    
                for(int i = k; i < k+k; i++)
                    if( !vis[i] ) flag = false;
                if(flag) {    printf("k = %d, m = %d\n", k, m); break; }
            }
        }
        return 0;
    }
    */
    int ans[15] = {0,2,7,5,30,169,441,1872,7632,1740,93313,459901,1358657,2504881};
    int main()
    {
        int k;
        while( scanf("%d",&k) ,k )
            printf("%d\n", ans[k] );
        return 0;
    }
  • 相关阅读:
    C#-创建自定义双击事件
    C#-设置button颜色
    C#-动态生成40个按钮,大小(20,20),要求每行6个放置
    推荐系统相关算法(1):SVD
    SVD在推荐系统中的应用
    个性化推荐研究(四)之如何利用用户行为数据
    推荐系统开源软件列表
    推荐系统中所使用的混合技术介绍
    协同过滤算法
    转:[大数据竞赛]算法讨论
  • 原文地址:https://www.cnblogs.com/yefeng1627/p/2842443.html
Copyright © 2011-2022 走看看