zoukankan      html  css  js  c++  java
  • HIT暑期集训 数论基础

    (数论菜鸡,全程划水)

    B    POJ 1061

    这题,一眼看出是考扩展欧几里得,然而搞了半天没搞出来,WA了一片。。最后心态爆炸。。。(代码就那么短)

    #include<cstdio>
    typedef long long ll;
    ll exgcd(ll a,ll b,ll & x,ll & y)
    {
        if (b==0)
        {
            x=1;y=0;
            return a;
        }
        ll g=exgcd(b,a%b,x,y);
        ll t=x;
        x=y;
        y=t-a/b*y;
        return g;
    }
    int main()
    {
        ll xx,yy,m,n,L,x,y,a,b,c,g,t;
        while (scanf("%lld%lld%lld%lld%lld",&xx,&yy,&m,&n,&L)!=EOF)
        {
            a=n-m;
            b=L;
            c=xx-yy;
            g=exgcd(a,b,x,y);
            if (c%g!=0) printf("Impossible
    ");
            else 
            {
                x=x*c/g;
                //x+b/g*t为通解 
                t=(-x)*g/b; 
                x=x+b/g*t;
                if (x<0) x+=b/g;
                printf("%lld
    ",x);
            } 
        }
        return 0;
    }
    View Code

    D    HDU 4135

    求区间内与N互质数的个数

    由于区间范围很大(1015),for循环一个个判断是不是质数的方法不可行。于是想到先求区间内不与N互质的数的数量。先对N分解质因子,应用容斥原理就可以算出区间内与N不互质的数的数量。

    应用容斥原理有几种实现方法,这里用的是位运算的方法。(感觉比较简洁好理解)

    #include<cstdio>
    #define maxn 100005
    int not_prime[maxn],prime[maxn],cnt;
    int q[maxn<<1],l;
    void pri(int n)
    {
        int i,j;
        for (i=2;i<=n;i++)
        {
            if (!not_prime[i]) prime[++cnt]=i;
            for (j=1;j<=cnt && i*prime[j]<=n;j++)
            {
                not_prime[i*prime[j]]=1;
                if (i%prime[j]==0) break; 
            }
        }    
    }
    void find(int n)
    {
        for (int i=1;i<=cnt && n>prime[i];i++)
            if (n%prime[i]==0)
            {
                while (n%prime[i]==0) n/=prime[i];
                q[l++]=prime[i];
            }
        if (n!=1) q[l++]=n;
    }
    long long work(long long x)
    {
        int i,j,tot=0;
        long long tmp,res=0;
        for (i=1;i<(1<<l);i++)
        {
            tmp=1;tot=0;
            for (j=0;j<l;j++)
                if ((i>>j)&1)
                {
                    tot++;
                    tmp*=q[j];
                }
            if (tot&1) res+=x/tmp;
            else res-=x/tmp;
        }
        return x-res;
    }
    int main()
    {
        int i,k,t,n;
        long long a,b;
        pri(100000);
        scanf("%d",&t);
        for (k=1;k<=t;k++)
        {
            scanf("%lld%lld%d",&a,&b,&n);
            l=0;
            find(n);
            printf("Case #%d: %lld
    ",k,work(b)-work(a-1));
        }
        return 0; 
    }
    View Code

    F    CodeForces 963A

    一个求逆元+快速幂+等比数列求和的问题。先计算出0-k-1项的和,就是首项a1,公比q就是(b/a)k。然后就是等比数列求和(anq-a1)/(q-1),注意要特判q=1的情况。

    #include<cstdio>
    #define mod 1000000009
    #define maxn 100005
    typedef long long ll;
    char s[maxn];
    ll pow(ll x,ll k)
    {
        ll re=1;
        while (k)
        {
            if (k&1) re=re*x%mod;
            k>>=1;
            x=x*x%mod; 
        }
        return re;
    }
    int main()
    {
        int i,n,k,now;
        ll a,b,q,inva,invq,ans,tmp;
        while (scanf("%d%lld%lld%d%s",&n,&a,&b,&k,s)!=EOF)
        {
            ans=0;
            for (i=0;i<k;i++)
            {
                tmp=pow(a,n-i)*pow(b,i)%mod;
                if (s[i]=='-') ans=(ans-tmp+mod)%mod;
                else ans=(ans+tmp)%mod; 
            }        
            now=(n+1)/k;
            inva=pow(a,mod-2);
            q=pow(inva,k)*pow(b,k)%mod;
            if (q==1)
            {
                ans=ans*now%mod; 
            }
            else
            {
                invq=pow(q-1,mod-2);
                ans=ans*(pow(q,now)-1)%mod*invq%mod; 
            }
            printf("%lld
    ",ans);
        }
        return 0;
     } 
    View Code

    G    HDU 6608

    先用Miller_Rabin判素数找出q,

    由威尔逊定理可知(p-2)!≡1 (mod p),而当q小于p-2时,有k*q!=(p-2)!(k=(p-2)*(p-1)......*(q+1))。

    因此只要求出k的逆元,k的逆元就是答案了。

    注意需要使用快速乘。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    #include<time.h>
    #define maxn 1000005
    using namespace std;
    typedef long long ll;
    ll qmul(ll a,ll b,ll mod)
    {
        ll ans=0;
        a=a%mod;
        while (b)
        {
            if (b&1) ans=(ans+a)%mod;
            a=(a+a)%mod;
            b>>=1;
        }
        return ans;
    }
    ll qpow(ll a,ll b,ll mod)
    {
        ll ans=1;
        a=a%mod;
        while (b)
        {
            if (b&1) ans=qmul(ans,a,mod);
            a=qmul(a,a,mod);
            b>>=1;
        }
        return ans;
    }
    int MR(ll n,int repeat)
    {
        if (n==2 || n==3) return 1;
        int i,j,s=0;
        ll d=n-1;
        while (!(d&1))
        {
            s++;
            d>>=1;
        }
        for (i=0;i<repeat;i++)
        {
            ll a=rand()%(n-3)+2;
            ll x=qpow(a,d,n);
            ll y=0;
            for (j=0;j<s;j++)
            {
                y=qmul(x,x,n);
                if (y==1 && x!=1 && x!=n-1) return 0;
                x=y;
            }
            if (y!=1) return 0;
        }
        return 1;    
    }
    int main()
    {
        int T;
        ll i,n,now,q;
        srand(time(NULL));
        scanf("%d",&T);
        while (T--)
        {
            scanf("%lld",&n);
            for (q=n-1;q;q--)
                if (MR(q,50)) break;
            now=1;
            if (q>=n-2) 
            {
                for (i=q;i>n-2;i--) now=now*i%n;
            }
            else 
            {
                for (i=q+1;i<=n-2;i++) now=qmul(now,qpow(i,n-2,n),n);
            }
            printf("%lld
    ",now);
        }
        return 0;
    } 
    View Code

    H    CodeForces 451E

    题意:有n种花,其中第i种花有f[i]支,要从这些盒子中取s支花,问有几种取法。

    思路:问题可以看作将s个球放入n个盒子中,第i个盒子中的球不超过f[i]个。

    如果没有f的限制,利用隔板法可知有C(s+n-1,n-1)种取法。

    当第i个盒子中的球超过f[i]时,此时不合法的方案数为C(s-f[i]-1+n-1,n-1)

    当第i,j个盒子中的球分别超过f[i],f[j]时,此时不合法的方案数为C(s-f[i]-1-f[j]-1+n-1,n-1),以此类推。

    于是就可使用容斥原理计算方案数了,ans=C(s+n-1,n-1)-ΣC(s-f[i]-1+n-1,n-1)+∑C(s-f[i]-1-f[j]-1+n-1,n-1)-......(-1)s∑C(s-∑(f[i]+1)+n-1,n-1)

    这里由于s较大,求组合数使用了Lucas定理,即C(n,m)%p=Lucas(n,m,p)=Lucas(n/p,m/p,p)*C(n%p,m%p,p)%p。

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cmath>
    using namespace std;
    typedef long long ll;
    const ll p=1e9+7;
    ll f[25];
    ll qpow(ll x,ll k,ll p)
    {
        ll re=1;
        while (k)
        {
            if (k&1) re=re*x%p;
            k>>=1;
            x=x*x%p;
        }
        return re;
    }
    ll get_c(ll n,ll m,ll p)
    {
        if (n<m) return 0;
        if (m>n-m) m=n-m;
        ll i,s1=1,s2=1;
        for (i=0;i<m;i++) 
        {
            s1=s1*(n-i)%p;//n!/(n-m)!
            s2=s2*(i+1)%p;//m! 
        }
        return s1*qpow(s2,p-2,p)%p;
    }
    ll lucas(ll n,ll m,ll p)
    {
        if (m==0) return 1;
        return lucas(n/p,m/p,p)*get_c(n%p,m%p,p)%p;
    }
    ll solve(ll n,ll s,ll p)
    {
        ll i,j,ans=0,sign,sum;
        for (i=0;i<(1<<n);i++) 
        {
            sign=1;
            sum=s;
            for (j=0;j<n;j++) 
                if ((i>>j)&1) 
                {
                    sum=sum-f[j]-1;
                    sign*=-1;
                }
            if (sum<0) continue;
            ans+=sign*lucas(sum+n-1,n-1,p);
            ans=ans%p;
        }
        return (ans+p)%p;
    }
    int main()
    {
        ll i,n,s;
        scanf("%lld%lld",&n,&s);
        for (i=0;i<n;i++) scanf("%lld",&f[i]);
        printf("%lld
    ",solve(n,s,p));
        return 0;
    } 
    组合数,容斥
  • 相关阅读:
    Postgresql修改序列方法 select setval('dataset_id_seq',1,false);
    再谈Elasticsearch全文搜索:你不知道的query_string、match、term、match_phrase的区别
    crosstab(unknown, unknown) does not exist
    Elasticsearch High Level Rest Client 发起请求的过程分析
    提取经过Jar2Exe编译加密的源代码 教程1
    Unity 协程与线程
    box unboxing(装箱 拆箱) C#编程指南
    C++基础笔记(四)C++内存管理
    C++ 箭头-> 双冒号:: 点号.操作符区别
    ref out 方法参数
  • 原文地址:https://www.cnblogs.com/lsykk/p/13412253.html
Copyright © 2011-2022 走看看