HDU 1573 X问题 (中国剩余定理 模线性方程组)
2016-08-17 16:59
393 查看
思路:
方法1:
问题可以转化为求模线性方程组。设要求得的满足方程组的最小正整数为n;
n=b1(moda1)
n=b2(moda2)
可以得到: n=b1+x∗a1=b2+y∗a2
变形之后:
x∗a1−y∗a2=b2−b1(1)
对式(1),我们可以看作 x∗a1=b2−b1(moda2)
对于这个式子,我们可以用扩展欧几里得算法求得x的最小整数解。所以最小正整数为 n0=b1+a1∗x
我们求解出来的n0加上k倍的a2绝对满足题意,但是却有可能少解,原因就是(1)式左右两边说不定可以约分,这样的话作为周期的 a2 就不对了。所以要这样解决:
n=n0+k∗a2/gcd(a1,a2)(k为任意整数)(moda1∗a2/gcd(a1,a2))
以上,我们就完成了对两个模线性方程的合并,接下来只要逐一进行合并,到最后只剩一个模线性方程时,就能很轻易地求解了。
#include <iostream> #include <cstdio> typedef long long int lli; using namespace std; int a[12]; int b[12]; lli egcd(lli a,lli b,lli &x,lli &y){ if(b == 0){ x = 1; y = 0; return a; } lli ans = egcd(b,a%b,x,y); lli temp = x; x = y; y = temp - a/b*x; return ans; } int main() { int t; int n,num; cin>>t; while(t--){ scanf("%d%d",&n,&num); for(int i = 1;i <= num;i++){ scanf("%d",a+i); } for(int i = 1;i <= num;i++){ scanf("%d",b+i); } lli b1 = b[1]; lli a1 = a[1]; lli x,y; int flag = 0; for(int i = 2;i <= num;i++){ lli gcd = egcd(a1,a[i],x,y); if((b[i]-b1) % gcd != 0){//这是模线性方程有整数解的充要条件 flag = 1; break; } lli temp = a[i]/gcd; x = x * (b[i]-b1)/gcd; // 也是约分的问题 x = (x%(temp) + temp) % (temp); b1 = b1 + a1*x; a1 = a1 * temp;// a1 = a1*a[i]/gcd } if(flag == 1 || n < b1){ puts("0"); } else{ lli ans = (n-b1)/a1 + 1; if(b1 == 0) ans--; printf("%I64d\n",ans); } } return 0; }
方法2:
先求出所有 ai 的最小公倍数lcm,我们可知,如果有某个数x满足题目的性质的话,那么x+lcm也一定满足题目的描述。所以我们对于n%lcm+1到n%lcm + lcm这个区间里暴力搜索是否有满足条件的解。如果有那么sum += n/lcm,注意:每有一个,都要加一回n/lcm。最后在1到n%lcm这个区间在找有没有符合的数,有的话,+1。
#include <iostream> #include <cstdio> typedef long long int lli; using namespace std; int a[12]; int b[12]; lli gcd(lli a,lli b){ return b == 0 ? a : gcd(b,a%b); } int main(){ int t; int n,num; cin>>t; while(t--){ scanf("%d%d",&n,&num); lli lcm = 1; for(int i = 1;i <= num;i++){ scanf("%d",a+i); lcm = lcm * a[i] / gcd(lcm,a[i]); } for(int i = 1;i <= num;i++){ scanf("%d",b+i); } lli ans = 0; int flag; for(int i = n%lcm + 1;i <= n%lcm + lcm;i++){ flag = 1; for(int j = 1;j <= num;j++){ if(i%a[j] != b[j]){ flag = 0; break; } } if(flag == 1){ ans += n/lcm; } } for(int i = 1;i <= n%lcm;i++){ flag = 1; for(int j = 1;j <= num;j++){ if(i%a[j] != b[j]){ flag = 0; break; } } if(flag == 1){ ans += 1; } } printf("%I64d\n",ans); } return 0;}
相关文章推荐
- android 后台service 实现无预览页面录像功能
- Swift完成fizz buzz test
- 解决eclipse中出现Resource is out of sync with the file system问题
- Laravel 5.2 教程 - 邮件
- java 通过下载地址下载到本地
- Android自定义View(二)画一个表
- Oracle高级查询
- mtk平台rom编译注意事项
- 删除IntelliJ Idea中Maven Archetype
- jQuery闭包
- tableView 局部刷新
- 好用的asp防SQL注入代码
- 【计算机网络-6】 【第三章】数据链路层笔记3——海明校验码
- hdu 1520 Anniversary party(入门树形DP)
- Swift完成fizz buzz test
- Swift完成fizz buzz test
- NodeJs系列二:你好,世界
- AOP技术基础
- 伸缩菜单(同时淡入淡出)
- (数组练习)二分法查找