Spoj数论专场解题报告

Spoj数论专题http://acm.hust.edu.cn:8080/judge/contest/view.action?cid=8035#overview 

Problem A

SPOJ ARCTAN

Use of Function Arctan

Problem B

SPOJ CUTSQRS

Cutting off Squares

Problem C

SPOJ FINFRAC

Finding Fractions

Problem D

SPOJ GPINTRI

Grid Points in a Triangle

Problem E

SPOJ PROOT

Primitive Root

Problem F

SPOJ RNG

Random Number Generator

Problem G

SPOJ EQU2

Yet Another Equation

Problem H

SPOJ BARB

Barbarians

Problem I

SPOJ DIVSUM

Divisor Summation

Problem J

SPOJ ETF

Euler Totient Function

Problem K

SPOJ GCDEX

GCD Extreme

Problem L

SPOJ INTEGER1

Power of Integer

Problem M

SPOJ KPEQU

Equation

Problem N

SPOJ MOD

Power Modulo Inverted

Problem O

SPOJ PAYING

Paying in Byteland

Problem P

SPOJ PGCD

Primes in GCD Table

Problem Q

SPOJ PRIME1

Prime Generator

Problem R

SPOJ ROBOT

Robot Number M

Problem S

SPOJ TAN1

Tan and His Interesting Game

Problem T

SPOJ SSORT

Silly Sort

注意:g++4.3.2与g++4.0.0-8,在头文件等方面有点差异,会导致AC的代码选另一语言会CE之类的错误。

Problem A

 http://blog.csdn.net/wxfwxf328/article/details/7555054

#include
using namespace std;
long long t,a,i;
int main(){
    cin>>t;
    while(t--){
        cin>>a;i=2*a;
        while((i*i+1)%(i-a))i--;
        cout<<(i*i+1)/(i-a)<

Problem B

原模型等价于在“1 1 1 1 1 1……”n1中间的n1个空格中选择任意个的不同选法的个数为2^(n-1)=c(n,0)+c(n,1)+c(n,2),其中win or lose的个数为2^(n-2)=c(n,0)+c(n,2)+……=c(n,1)+c(n,3)+……  tot=2^(x1-1)*2^(x2-1)*……=2^x

#include
#include
using namespace std;
void dfs(int a,int b,int &x,bool &win)
{
    if(ab
    if(b==0){x=0,win=0;return;}
    dfs(b,a%b,x,win);
    x+=a/b-1;//x=sum{xi-1};
    if(win==0||a/b>1) win=1;
    else win=0;
}
//len=log10(2^n)+1=n*log10(2)+1
int cal(int n)//2^n对应10进制长度
{
    if(n<=0) return 1;
    return floor(n*log10(2))+1;
}
int main()
{
    int t;cin>>t;
    while(t--)
    {
        int a,b;cin>>a>>b;
        int x;bool win;
        dfs(a,b,x,win);
        cout<<(win?5:6)+cal(x)+cal(x-1)<

Problem C

求满足a/b < p/q < c/d  的p,qq最小。

解法:连分数(自己baidu

#include
using namespace std;
typedef long long ll;
ll dfs(ll a,ll b,ll c,ll d)
{
    ll k=a/b;
    a-=b*k;c-=d*k;
    if(c>d) return 1;
    if(a==0) return d/c+1;
    return d*dfs(d,c,b,a)/c+1;
}
int main()
{
    ll a,b,c,d,y;
    while(scanf("%lld%lld%lld%lld",&a,&b,&c,&d)!=EOF)
    {
        y=dfs(a,b,c,d);
        printf("%lld/%lld\n",a*y/b+1,y);
    }
    return 0;
}

Problem D

求满足 y <= ax / b and x <= n的整点个数。

cd=ad%bd;ans(ade)=ans(cde)+ad/bd*ans(bde) 

ans(bde)可以直接求出

这格式真给力

#include
#include
using namespace std;
typedef long long ll;
int gcd(int a,int b)
{
    return b?gcd(b,a%b):a;
}
ll dfs(ll n,ll a,ll b)//RT三角 格点数
{
    ll t=n*(n+1)/2*(a/b);
    a%=b;
    if(a==0) return n+1+t;
    ll d=a*n/b;
    t+=(n+1)*(d+1)+d/a+1;//+平行四边形
    return t-dfs(d,b,a);//-三角形(与RT三角形等价)
}
int main()
{
    int t;cin>>t;
    while(t--){
        int n,a,b;
        scanf("%d%d%d",&n,&a,&b);
        int p=gcd(a,b);
        printf("%lld\n",dfs(n,a/p,b/p));
    }
    return 0;
}

Problem E

根据题目要求的来判断,直接暴力求出所有因子

#include
#include
#include
using namespace std;
typedef long long ll;
ll modPow(ll a,int n,int p)
{
    ll res=1;
    while(n)
    {
        if(n&1) res=res*a%p;
        n>>=1;a=a*a%p;
    }
    return res;
}
int fac[100000],cnt;
int main()
{
    int p,n;
    while(scanf("%d%d",&p,&n),n||p)
    {
        cnt=0;p--;
        int lim=sqrt(p+1);
        for(int i=2;i<=lim;i++) if(p%i==0)
            fac[cnt++]=i,fac[cnt++]=p/i;
        while(n--)
        {
            int x;
            scanf("%d",&x);
            bool fg=1;
            for(int i=0;i

Problem F

#include
#include
using namespace std;
//根据容斥
//ans=f(x,y,z)-f(-x,y,z)-f(x,-y,z)-f(x,y,-z)+f(-x,-y,z)+f(-x,y,-z)+f(x,-y,-z)-f(-x,-y,-z)
//f(i,j,k)为小于sum,且x,y,z在区间[-(x+y+z),sum]的情况 仅在f(i,j,k)>0时,有意义
//n维f(i,j,k)=s^n/(n!);二维s*s/2(面积) 三维s*s*s/6(体积)
//ans=ans/tot
int t,n,a,b,x[11];
double ans;
void dfs(int i,int sum,int k)
{
    if(i==n)
    {
        if(sum>0)
        {
            double x=1;
            for(int i=0;i>t;
    while(t--)
    {
        cin>>n>>a>>b;
        for(int i=0;i>x[i];
        double tot=1;
        for(int i=0;i


Problem G

Pell方程求最小解,此题需要高精度

解法:http://hi.baidu.com/toremote/item/d3b81ccee2b4330d0bd93a2e

证明:http://mathworld.wolfram.com/PellEquation.html

import java.math.BigInteger;
import java.util.Scanner;
//a[n]=(g[n]+a[0])/h[n]
//g[n]=a[n-1]*h[n-1]-g[n-1]
//h[n]=(N-g[n]*g[n])/h[n-1]
//p[n]=a[n-1]*p[n-1]+p[n-2]
//q[n]=a[n-1]*q[n-1]+q[n-2]
//so:
//p[n]*q[n-1]-p[n-1]*q[n]=(-1)^(n+1);
//p[n]^2-N*q[n]^2=(-1)^(n+1)*h[n+1];
public class Main {
    public static BigInteger p, q;
    public static void solve(int n) {
        BigInteger N, p1, p2, q1, q2, a0, a1, a2, g1, g2, h1, h2;
        g1 = q2 = p1 = BigInteger.ZERO;
        h1 = q1 = p2 = BigInteger.ONE;
        a0 = a1 = BigInteger.valueOf((long)Math.sqrt(1.0*n));
        N = BigInteger.valueOf(n);
        while (true) {
            g2 = a1.multiply(h1).subtract(g1);      //g2=a1*h1-g1
            h2 = N.subtract(g2.pow(2)).divide(h1);  //h2=(n-g2^2)/h1 
            a2 = g2.add(a0).divide(h2);             //a2=(g2+a0)/h2
            p = a1.multiply(p2).add(p1);            //p=a1*p2+p1
            q = a1.multiply(q2).add(q1);            //q=a1*q2+q1
            if (p.pow(2).subtract(N.multiply(q.pow(2))).compareTo(BigInteger.ONE) == 0) return;//p^2-n*q^2=1
            g1 = g2;h1 = h2;a1 = a2;
            p1 = p2;p2 = p;
            q1 = q2;q2 = q;
        }
    }
 
    public static void main(String[] args) {
        Scanner cin = new Scanner(System.in);
        int t=cin.nextInt();
        while (t--!=0) {
            solve(cin.nextInt());
            System.out.println(p + " " + q);
        }
    }
}


Problem H

此题时限有点紧

max(c[i])开始枚举每一个值,对任意i,j解同余方程判断,复杂度10*10*10^6*O(ext_gcd)

#include
#include
#include
#include
using namespace std;
int c[22],p[22],l[22];
int n,t,d;
int ex_eculid(int a,int b,int &x,int &y)
{
    if(b==0){x=1;y=0;return a;}
    d=ex_eculid(b,a%b,x,y);
    t=x;x=y;y=t-(a/b)*y;
    return d;
}
bool judge(int k)
{
    int x,y,a,b,d,m;
    for(int i=0; i>t;
    while(t--)
    {
        cin>>n;
        for(int i=0; ip[j]) swap(p[i],p[j]),swap(c[i],c[j]),swap(l[i],l[j]);
        int k=max(n,*max_element(c,c+n));
        while(!judge(k)) k++;
        cout<

Problem I

除了J题最水的一题,筛法求解

#include
using namespace std;
const int maxn=500000;
int a[maxn+10];
int main()
{
    for(int i=1;i<=maxn/2;i++)
        for(int x=i+i;x>t;
    while(t--)
    {
        scanf("%d",&x);
        printf("%d\n",a[x]);
    }
    return 0;
}

Problem J

#include
using namespace std;
const int maxn=1000001;
int phi[maxn];
int main()
{
    for(int i=0;i>t;
    while(t--)
    {
        int x;
        cin>>x;
        cout<


Problem K

题目描述有问题,spoj讨论里有。更正题目:http://uva.onlinejudge.org/external/114/11426.pdfn范围是1000000,其它相同)

把关系:a[x]=sum(a[i]*j){i*j=x}搞定即可

#include
using namespace std;
typedef long long ll;
const int maxn=1000010;
int e[maxn];//euler
ll a[maxn];//ans
//a[x]=sum(a[i]*j) {i*j=x}
int main()
{
    for(int i=1;i

Problem L

比较坑的一题,有个溢出的情况要注意:lim(a,i)=max{k|k^i<=a},会有可能出现类似ll(2^60)>ll(3^60)的情况,为此wa了无数次。

从2次方开始,每次的次方数k对应sig要减去其所有因子的sig值。

#include
#include
#include
#include
using namespace std;
typedef long long ll;
int sig[100];
ll ppow(ll a,ll n)
{
    ll b=1;
    while(n)
    {
        if(n&1) b=b*a;
        n>>=1;a=a*a;
    }
    return b;
}
ll lim(ll a,int i)//return max{k|k^i<=a}
{
    ll k=pow(a,1.0/i)-1;
    while(1)
    {
        if(pow(k*1.0,i*1.0)>1e18||ppow(k,i)>a) return k-1;
        k++;
    }
}
ll cal(ll a)
{
    ll ans=a;
    for(int i=2;;i++)
    {
        ll k=lim(a,i);
        if(k==1) return ans;
        ans+=(k-1)*sig[i];
    }
}
int main()
{
    for(int i=1;i<100;i++) sig[i]=i;
    for(int i=1;i<100;i++) for(int j=1;j>a>>b,a||b) cout<


Problem M

1/N! = 1/X + 1/Y 的解个数,对n!分解处理,需答案高精度。

//n!=p1^q1*p2^q2*...
//ans=(2*q1+1)*(2*q2+1)*...
//n!=x*y/(x+y) x中因子pi个数有2*qi+1个,对应y中pi因子个数唯一对应
#include
#include
using namespace std;
typedef long long ll;
const int maxn=10010;
int prm[maxn],cnt=0;
bool isprm[maxn];
struct bint
{
    int ln,v[260];
    bint(int r=0){
        memset(v,0,sizeof(v));
        for(ln=0;r>0;r/=10000) v[ln++]=r%10000;
    }
    bint operator * (const bint &b)const
    {
        bint res;res.ln=0;
        if(0==b.ln){res.v[0]=0;return res;}
        for(int i=0;i0;j++,cy/=10000)
            {
                if(j=res.ln) res.v[res.ln++]=cy%10000;
                else res.v[i+j]=cy%10000;
            }
        }
        return res;
    }
    void write()
    {
        printf("%d",ln==0?0:v[ln-1]);
        for(int i=ln-2;i>=0;i--) printf("%04d",v[i]);
        puts("");
    }
};
ll cal(int p,int n)
{
    ll ans=0;
    while(n)
    {
        n/=p;
        ans+=n;
    }
    return ans;
}
int main()
{
    memset(isprm,1,sizeof(isprm));cnt=0;
    for(int i=2;i>n,n)
    {
        bint ans=1;
        for(int i=0;prm[i]<=n;i++)
        {
            bint tmp(cal(prm[i],n)*2+1);
            ans=ans*tmp;
        }
        ans.write();
    }
    return 0;
}


Problem N

离散对数 k%z = xy%z ,大致方法为枚举1~sqrt(z),求出x^(i)%z,则ans=(i*sqrt(z)+j),然后枚举i,二分或者Hash找对应的j。这一题lp模板不可以用,因为不满足xz互素。

#include
#include
#include
using namespace std;
typedef long long ll;
const int maxn=65535;
struct Hash
{
    int a,b,nxt;
}hash[maxn<<1];
int flg[maxn];
int top,idx;
void ins(int a,int b)
{
    int k=b&maxn;
    if(flg[k]!=idx)
    {
        flg[k]=idx;
        hash[k].nxt=-1;
        hash[k].a=a;
        hash[k].b=b;
        return;
    }
    while(hash[k].nxt!=-1)
    {
        if(hash[k].b==b) return ;
        k=hash[k].nxt;
    }
    hash[k].nxt=++top;
    hash[top].nxt=-1;
    hash[top].a=a;
    hash[top].b=b;
}
int find(int b)
{
    int k=b&maxn;
    if(flg[k]!=idx) return -1;
    while(k!=-1)
    {
        if(hash[k].b==b) return hash[k].a;
        k=hash[k].nxt;
    }
    return -1;
}
int gcd(int a,int b){return b?gcd(b,a%b):a;}
int ext_gcd(int a,int b,int &x,int &y)
{
    int t,ret;
    if(!b) {x=1,y=0;return a;}
    ret=ext_gcd(b,a%b,x,y);
    t=x,x=y,y=t-a/b*y;
    return ret;
}
int inv(int a,int b,int n)
{
    int x,y,e;
    ext_gcd(a,n,x,y);
    e=(ll)x*b%n;
    return e<0?e+n:e;
}
int pow_mod(ll a,int b,int c)
{
    ll ret=1%c;a%=c;
    while(b)
    {
        if(b&1) ret=ret*a%c;
        a=a*a%c;b>>=1;
    }
    return ret;
}
int baby(int A,int B,int C)
{
    top=maxn;++idx;
    ll buf=1%C,D=buf,k;
    int i,d=0,tmp;
    for(i=0;i<=100;buf=buf*A%C,i++) if(buf==B) return i;
    while((tmp=gcd(A,C))!=1)
    {
        if(B%tmp)return -1;
        ++d;
        C/=tmp;B/=tmp;
        D=D*A/tmp%C;
    }
    int M=(int)ceil(sqrt((double)C));
    for(buf=1%C,i=0;i<=M;buf=buf*A%C,i++) ins(i,buf);
    for(i=0,k=pow_mod((ll)A,M,C);i<=M;D=D*k%C,i++)
    {
        tmp=inv((int)D,B,C);
        int w;
        if(tmp>0&&(w=find(tmp))!=-1) return i*M+w+d;
    }
    return -1;
}
int main()
{
    int a,b,c;
    while(scanf("%d%d%d",&a,&c,&b),a||b||c)
    {
        b%=c;
        int ans=baby(a,b,c);
        if(ans<0) puts("No Solution");
        else printf("%d\n",ans);
    }
    return 0;
}


Problem O

每个硬币值为2^i,求最少用多少硬币表示n

N=a*1+b*2+c*4+d*8+e*16+……{a,b,c,d,e取值为01},答案即为此数对应2进制中1的个数。

#include
using namespace std;
struct bint
{
    int ln,v[300];
    bint(int r=0){
        memset(v,0,sizeof(v));
        for(ln=0;r>0;r/=10000) v[ln++]=r%10000;
    }
    bool operator < (const bint &b)const
    {
        int i;if(ln!=b.ln) return ln=0&&v[i]==b.v[i];i--);
        return i<0?0:v[i]0&&res.v[res.ln-1]==0) res.ln--;
        return res;
    }
    bint operator * (const bint &b)const
    {
        bint res;res.ln=0;
        if(0==b.ln){res.v[0]=0;return res;}
        for(int i=0;i0;j++,cy/=10000)
            {
                if(j=res.ln) res.v[res.ln++]=cy%10000;
                else res.v[i+j]=cy%10000;
            }
        }
        return res;
    }
    bool read(char *buf)
    {
        int w,u,lnn=strlen(buf);
        memset(v,0,sizeof(v));
        if(48==buf[0]&&0==buf[1]) return 1;
        for(w=1,u=0;lnn;)
        {
            u+=(buf[--lnn]-48)*w;
            if(w*10==10000) v[ln++]=u,u=0,w=1;
            else w*=10;
        }
        if(w!=1) v[ln++]=u;
        return 1;
    }
    void write()
    {
        printf("%d",ln==0?0:v[ln-1]);
        for(int i=ln-2;i>=0;i--) printf("%04d",v[i]);
        puts("");
    }
}a[5000];
char s[1111];
int main()
{
    a[0]=1;
    for(int i=1;i<3500;i++) a[i]=a[i-1]*2;
    bint n;
    int t;
    cin>>t;getchar();
    while(t--)
    {
        gets(s);
        n.read(s);
        int i=0,ans=0;
        bint zero(0);
        while(zero

Problem P

所有题目中时限最长的一道,但还有卡时间的。

//参考自前人代码,表示压力山大
//ans(x,y)=ans(min(x,y),min(x,y))
#include
using namespace std;
const int maxn=10000010;
int a[maxn];//质因子个数
int b[maxn];//出现次数为2的质因子的个数(仅需要处理出0、1、其它)
int c[maxn];//c[i]为gcd(x,y)<=i的总个数
void init()
{
    for(int i=2; i1) c[i]=0;
        else if(b[i]==1) c[i]=a[i]&1?-1:1;
        else c[i]=a[i]&1?a[i]:-a[i];
        c[i]+=c[i-1];//将c数组处理为前n个之和
    }
}
int main()
{
    init();
    int t;cin>>t;
    while(t--)
    {
        int a,b,m,aa,bb;
        cin>>a>>b;
        m=min(a,b);
        long long ans=0;
        for(int i=2;i<=m;i++)
        {
            aa=a/i;bb=b/i;
            int t=min(a/aa,b/bb)-i;//a/aa:使a/i相同的i的个数
            ans+=(long long)(c[i+t]-c[i-1])*aa*bb;
            i+=t;//直接跳到下一个不同i
//          ans+=(c[i]-c[i-1])*(a/i)*(b/i);直接这样计算可出答案,但会超时
        }
        cout<

Problem Q

另一道水题,打个区间素数表

#include
using namespace std;
int t;
const int maxn=120000;
bool isprm[maxn];
int prm[maxn],cnt=0;
int vis[maxn*3];
int main()
{
    memset(isprm,1,sizeof(isprm));
    for(int i=2;i40000) continue;
        for(int j=i*i;j>t;
    while(t--)
    {
        cin>>m>>n;
        memset(isprm,1,n-m+1);
        for(int i=0;prm[i]*prm[i];i++)
            for(int j=m/prm[i];j*prm[i]<=n;j++)
                if(j>1&&j*prm[i]>=m) isprm[j*prm[i]-m]=0;
        for(int i=m;i<=n;i++) if(i>1&&isprm[i-m]) printf("%d\n",i);
        if(t) cout<

Problem R

比较简单题,主要就是所有约数的欧拉数之和等于它本身,由(黑书229)性质可以容易得出

N=p^x,M=q^y

Sum(euler(pi))=1+(p-1)*(1+p+p^2+……+p^(x-1))=p^x

Sum(euler(qi))=1+(p-1)*(1+q+q^2+……+q^(y-1))=q^y

因此:Sum(euler(pi))*Sum(euler(qi))=p^x*q^y=M*N,得证

#include
#include
#include
using namespace std;
//sum(euler(i))=x {gcd(i,M)=i}
//a+b+c+1=sum(euler(i))=x
const int mod=10000;
int p[1111],a[1111],dp[1111];
int modpow(int a,int n)
{
    int ans=1;
    while(n)
    {
        if(n&1) ans=ans*a%mod;
        a=a*a%mod;n>>=1;
    }
    return ans;
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        int n;
        cin>>n;
        for(int i=1;i<=n;i++) scanf("%d%d",p+i,a+i);
        int m=1;
        for(int i=1;i<=n;i++) m=m*modpow(p[i],a[i])%mod;
        int s=p[1]==2?2:1;
        memset(dp,0,sizeof(dp));dp[0]=1;
        for(int i=(p[1]==2?2:1);i<=n;i++)
            for(int j=n;j>0;j--)
                dp[j]=(dp[j]+dp[j-1]*(p[i]-1))%mod;
        int x=0,y=0;
        for(int i=1;i<=n;i++) i&1?x+=dp[i]:y+=dp[i];
        x%=mod;y%=mod;
        cout<

Problem S

20题里唯一没过的一题。

Problem T

//黑书248
#include
using namespace std;
int a[1<<10],p[1<<10];
int main()
{
    int n,cas=1;
    while(cin>>n,n)
    {
        for(int i=0;i

你可能感兴趣的:(acm,专题,spoj,数论)