数学专题训练2
2014-02-26 22:02
218 查看
这次是数论专题训练。。。这几道题总的来说:没有最坑,只有更坑!!
题目一:Uva11426
题目大意:输入正整数 n 求Σgcd(i,j) 1<=i<j<=n
设f
=gcd(1,n)+gcd(2,n)+gcd(3,n)...+gcd(n-1,n);
那么答案自然就是 Σf[i] 2<=i<=n
关键问题在这个f
怎么求上。。。
假设gcd(x,n)=i;那么 gcd(x/i,n/i)=1;
符合这个条件的x的个数实际上就是小于x/i且与x/i互质的数的个数-->欧拉函数
于是f
=Σ phi(n/i)( i为n的因子切i != n)
如果对于每个f[i]枚举它的因子。。。复杂度高了
于是就这样处理:枚举因子i,再枚举它的倍数j(i<j<=n)。。。f[j]+=phi[j/i];
...这道题就解决了
然后要注意的一个地方是有多组输入。。。但是每次输出的答案的值都可以一次性求出来。。于是就先预处理出来 :s
表示输入为n时的答案。。。。明显s
=s[n-1]+f
;
至于如何求出phi。。。也是先用一个类似筛法的方法预处理一次。。注意phi[1]=1; (我开始只预处理了phi,T了半天。。。)
题目二: LA4119 Always an integer
题目大意:输入一个次数严格递减的多项式,判断它是否总是整数。。多项式输入格式(只有整数多项式)/某个数
。。。根据lrj《算法入门经典训练指南》的分析, 只需要把1到最高次+1 带进去判断就行了。。。如果出现了分子多项式值 % 分母!=0 就不是。。否则就是。。
具体分析还是有很长。。就不写了
其实这道题难点还在字符串处理上。。。我一直wa,最后靠对拍才完全弄对。。
题目三:wikioi1213 解的个数
这个嘛。。。其实差不多是扩展欧几里得的裸题。。。不过数据有些坑点。。。特殊情况比较多,还会爆int。。。所以也卡了半天。。。最后看数据才改对的。。。
注意的是:解的个数并不需要枚举。。而是直接一步求出边界(可能需要调整)。。。
题目四:poj 2891
中国剩余定理的裸题。。。不过这个的a不都互素。。。所以要一次合并两个方程。。
对于:
x=r1(mod a1)
x=r2(mod a2)
可以写成 x=r1+a1*k1=r2*a2*k2
然后用扩展欧几里得解 a1*k1-a2*k2=r2-r1的k的最小正值
然后带回x=r1+a1*k1得到x的解。。。
于是方程就被合并成了 x=r1+a1*k1(mod (a1,a2))。。。
当有多个方程组的时候把先两个合并成一个,再把它拿来继续和下一个方程合并。。。
合并到最后答案也就很明显了- -
注意写的代码里面a,r是反的。。。
题目一:Uva11426
题目大意:输入正整数 n 求Σgcd(i,j) 1<=i<j<=n
设f
=gcd(1,n)+gcd(2,n)+gcd(3,n)...+gcd(n-1,n);
那么答案自然就是 Σf[i] 2<=i<=n
关键问题在这个f
怎么求上。。。
假设gcd(x,n)=i;那么 gcd(x/i,n/i)=1;
符合这个条件的x的个数实际上就是小于x/i且与x/i互质的数的个数-->欧拉函数
于是f
=Σ phi(n/i)( i为n的因子切i != n)
如果对于每个f[i]枚举它的因子。。。复杂度高了
于是就这样处理:枚举因子i,再枚举它的倍数j(i<j<=n)。。。f[j]+=phi[j/i];
...这道题就解决了
然后要注意的一个地方是有多组输入。。。但是每次输出的答案的值都可以一次性求出来。。于是就先预处理出来 :s
表示输入为n时的答案。。。。明显s
=s[n-1]+f
;
至于如何求出phi。。。也是先用一个类似筛法的方法预处理一次。。注意phi[1]=1; (我开始只预处理了phi,T了半天。。。)
#include<cstdio> #include<cmath> #include<iostream> #include<cstring> #include<cstdlib> #include<algorithm> using namespace std; #define maxn 4000000 int n; long long f[maxn+10],s[maxn+10]; int phi[maxn+10]; void make_phi(int u) { phi[1]=1; for(int i=2;i<=u;i++) { if(!phi[i]) for(int j=i;j<=u;j+=i) { if(!phi[j])phi[j]=j; phi[j]=phi[j]/i*(i-1); } } } int main() { make_phi(maxn); for(int i=1;i<=maxn;i++) { for(int j=i*2;j<=maxn;j+=i) { f[j]+=(long long)i*phi[j/i]; } } s[2]=f[2]; for(int i=3;i<=maxn;i++)s[i]+=s[i-1]+f[i]; while(scanf("%d",&n)!=EOF) { if(n==0)break; cout<<s <<endl; } return 0; }
题目二: LA4119 Always an integer
题目大意:输入一个次数严格递减的多项式,判断它是否总是整数。。多项式输入格式(只有整数多项式)/某个数
。。。根据lrj《算法入门经典训练指南》的分析, 只需要把1到最高次+1 带进去判断就行了。。。如果出现了分子多项式值 % 分母!=0 就不是。。否则就是。。
具体分析还是有很长。。就不写了
其实这道题难点还在字符串处理上。。。我一直wa,最后靠对拍才完全弄对。。
#include<cstdlib> #include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; long long mod; int k; char temp[1000+10]; long long A[100+10]; long long qpow(long long d,int c) { if(c==0)return 1; if(c==1)return d; long long ret=qpow(d,c/2); ret=(ret*ret)%mod; if(c&1)ret=(ret*d)%mod; return ret; } bool check(int x) { long long ans=0; for(int i=k;i>=0;i--) { ans=(ans+(A[i]%mod*qpow(x,i)%mod)%mod)%mod; } if(ans%mod==0)return true; else return false; } int getk()//顺便把mod解决了吧 { int len=strlen(temp); int ret=0; int i; for(i=0;i<len;i++) { if(temp[i]=='n') { i++; if(temp[i]=='^') { i++; int tem=temp[i]-'0'; while(temp[i+1]<='9'&&temp[i+1]>='0') { i++; tem*=10; tem+=temp[i]-'0'; } A[tem]=1; if(tem>ret)ret=tem; } else A[1]=1;if(ret<1)ret=1; } else if(temp[i]=='-'||temp[i]<='9'&&temp[i]>='0') { long long tem1=0; bool f=0; if(temp[i]=='-')f=1; else tem1=temp[i]-'0'; if(f&&!(temp[i+1]<='9'&&temp[i+1]>='0'))tem1=1; while(temp[i+1]<='9'&&temp[i+1]>='0') { i++; tem1*=10; tem1+=temp[i]-'0'; } if(f)tem1=-tem1; if(temp[i+1]=='n') { i++; if(temp[i+1]=='^') { i+=2; int tem=temp[i]-'0'; while(temp[i+1]<='9'&&temp[i+1]>='0') { i++; tem*=10; tem+=temp[i]-'0'; } ret=max(ret,tem); A[tem]=tem1; } else { A[1]=tem1; if(ret<1)ret=1; } } else { A[0]=tem1; } } if(temp[i]=='/')break; } long long tem=0; for(i=i+1;i<len;i++) { tem*=10; tem+=temp[i]-'0'; } mod=tem; return ret; } int main() { int cas=0; while(scanf("%s",temp)!=EOF) { cas++; if(temp[0]=='.')break; memset(A,0,sizeof(A)); k=getk(); bool ok=true; for(int i=0;i<=k+1;i++) { if(!check(i)) { ok=false; break; } } if(ok)printf("Case %d: Always an integer\n",cas); else printf("Case %d: Not always an integer\n",cas); } return 0; }
题目三:wikioi1213 解的个数
这个嘛。。。其实差不多是扩展欧几里得的裸题。。。不过数据有些坑点。。。特殊情况比较多,还会爆int。。。所以也卡了半天。。。最后看数据才改对的。。。
注意的是:解的个数并不需要枚举。。而是直接一步求出边界(可能需要调整)。。。
#include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> #include<iostream> using namespace std; int n; long long a,b,c,p,q,r,s; bool special() { if(a==0) { if(b==0) { if(c==0) printf("%lld\n",(q-p+1)*(s-r+1)); else printf("0\n"); return true; } else { if(c%b==0&&r<=(-c)/b&&(-c)/b<=s)printf("%lld\n",q-p+1); else printf("0\n"); return true; } } else if(b==0) { if(c%a==0&&p<=(-c)/a&&(-c)/a<=q)printf("%lld\n",s-r+1); else printf("0\n"); return true; } return false; } long long gcd(int a,int b) { if(b==0)return a; else return gcd(b,a%b); } void exgcd(long long &x,long long &y,long long a,long long b) { if(b==0) { x=1;y=0; } else { exgcd(y,x,b,a%b); y-=x*(a/b); } } int main() { cin>>n; while(n--) { cin>>a>>b>>c>>p>>q>>r>>s; if(p>q||r>s){printf("0\n");continue;} if(special())continue;//特判 long long d=gcd(a,b); if(c%d!=0)printf("0\n"); else { long long a1=a/d,b1=b/d,c1=c/d; long long x,y; exgcd(x,y,a1,b1); x=x*(-c1); y=y*(-c1); //计算x的边界,用其限制y的边界 long long xmax=(q-x)/b1*b1+x,xmin=x-(x-p)/b1*b1; if(xmax>q)xmax-=b1; if(xmin<p)xmin+=b1; if(xmax<xmin){printf("0\n");continue;} long long ymax=(-c1-a1*xmax)/b1,ymin=(-c1-a1*xmin)/b1; if(ymax<ymin)swap(ymax,ymin); //计算y的边界 long long ymax1=(s-y)/a1*a1+y,ymin1=y-(y-r)/a1*a1; if(ymin1<r)ymin1+=a1; if(ymax1>s)ymax1-=a1; if(ymax1<ymin1){printf("0\n");break;} //合并范围 ymax=min(ymax,ymax1); ymin=max(ymin,ymin1); printf("%lld\n",(ymax-ymin)/abs(a1)+1); } } return 0; }
题目四:poj 2891
中国剩余定理的裸题。。。不过这个的a不都互素。。。所以要一次合并两个方程。。
对于:
x=r1(mod a1)
x=r2(mod a2)
可以写成 x=r1+a1*k1=r2*a2*k2
然后用扩展欧几里得解 a1*k1-a2*k2=r2-r1的k的最小正值
然后带回x=r1+a1*k1得到x的解。。。
于是方程就被合并成了 x=r1+a1*k1(mod (a1,a2))。。。
当有多个方程组的时候把先两个合并成一个,再把它拿来继续和下一个方程合并。。。
合并到最后答案也就很明显了- -
注意写的代码里面a,r是反的。。。
#include<cstdio> #include<cstdlib> #include<cmath> #include<cstring> #include<iostream> #include<algorithm> using namespace std; typedef long long LL; LL n; LL aaa,rrr; LL a1,b1,c1; LL n1,m; LL nowa,nowr; LL gcd(LL a,LL b) { if(!b)return a; else return gcd(b,a%b); } LL exgcd(LL a,LL b,LL &x,LL &y) { if(b==0) { x=1;y=0;return a; } else { LL ret=exgcd(b,a%b,y,x); y-=x*(a/b); return ret; } } int main() { while(cin>>n) { cin>>nowr>>nowa; bool flag=false; for(LL i=2;i<=n;i++) { cin>>rrr>>aaa; if(flag)continue; a1=nowr,b1=rrr;c1=aaa-nowa; LL d=exgcd(a1,b1,n1,m); if(d==0||c1%d!=0) { flag=1; continue; } LL temp=b1/d; LL x=((n1*c1/d)%temp+temp)%temp; nowa=x*nowr+nowa; nowr=nowr/d*rrr; } if(!flag)cout<<nowa<<endl; else cout<<-1<<endl; } return 0; }
相关文章推荐
- 训练指南——数学专题一的总结
- 数学专题训练4
- 数学专题训练3
- 【【henuacm2016级暑期训练】动态规划专题 L】Civilization
- HDU上的专题训练(背包问题-线段树+树状数组+DP优化+网络流+字符匹配+最短路+矩阵
- 【测试】【最短路】图论专题训练--智捅马蜂窝
- 大白 数学专题 部分例题习题总结
- 蒟蒻DP专题训练2--HDU1231
- 复试训练——数学问题—— 素数筛选法
- 数学专题小记
- 搜索专题训练【CSUST_Newer_12级入门】
- DFS-BFS搜索专题【经典训练题】【有时间一个个做下来】
- CDOJ 训练搜索专题G 八数码固定终点问题
- 结束专题训练hdu1255
- HDU上的专题训练
- hdu 4923 Room and Moor(数学题)2014多校训练第6场
- 【 henuacm2016级暑期训练-动态规划专题 A 】Cards
- scau_专题1训练总结
- 【测试】图论专题训练--紧急援救
- 概率dp && 高斯消元 专题训练