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 |
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)<
原模型等价于在“1 1 1 1 1 1……”n个1中间的n-1个空格中选择任意个的不同选法的个数为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)<
求满足a/b < p/q < c/d 的p,q且q最小。
解法:连分数(自己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;
}
求满足 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;
}
根据题目要求的来判断,直接暴力求出所有因子
#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
#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
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);
}
}
}
此题时限有点紧
从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<
除了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;
}
#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<
题目描述有问题,spoj讨论里有。更正题目:http://uva.onlinejudge.org/external/114/11426.pdf(n范围是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
比较坑的一题,有个溢出的情况要注意: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<
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;
}
离散对数 k%z = xy%z ,大致方法为枚举1~sqrt(z),求出x^(i)%z,则ans=(i*sqrt(z)+j),然后枚举i,二分或者Hash找对应的j。这一题lp模板不可以用,因为不满足x与z互素。
#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;
}
每个硬币值为2^i,求最少用多少硬币表示n。
N=a*1+b*2+c*4+d*8+e*16+……{a,b,c,d,e取值为0或1},答案即为此数对应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
所有题目中时限最长的一道,但还有卡时间的。
//参考自前人代码,表示压力山大
//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<
另一道水题,打个区间素数表
#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<
比较简单题,主要就是所有约数的欧拉数之和等于它本身,由(黑书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<
20题里唯一没过的一题。
//黑书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