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;
} 
组合数,容斥
原文地址:https://www.cnblogs.com/lsykk/p/13412253.html