HDU4596 Yet another end of the world(数学公式推演+数论定理--2013南京邀请赛)
题目链接:
http://acm.hdu.edu.cn/showproblem.php?pid=4596
题目描述:
1)给N组数,每组X,Y,Z三个数
2)是否存在一个数ID使得同时满足:Yi<=ID%Xi<=Zi 和 Yj<=ID%Xj<=Zj
3) Y<=Z<X<2*10^9
解题思路:
数学题尽量用公式表示,利用公式推演出熟悉的式子,利用现成的已知的定理去解决,
即使不知道这个定理怎么去证明,自己解不出的题,就交给N多年前的前辈门吧,直接
使用他们的定理就行了,有兴趣的话可以以后慢慢证。
y1<= id%x1 <=z1
y2<= id%x2 <=z2
设id/x1=a1,id/x2=a2
id%x1=b1,id%x2=b2
则有:
id=a1x1+b1
id=a2x2+b2
a1x1+b1=a2x2+b2
=>a1x1-a2x2=b2-b1
线性不定方程组
有整数解的条件???
数论定理:线性方程组有整数解的条件是b2-b1是gcd(x1,x2)的整数倍数
另外我们来研究一下同一个数对不同数同时取余的规律:
可以发现:
1)同一个数对不同数a,b同时取余结果的周期是a,b的最小公倍数;
2)同一个数对不同数a,b同时取余结果的所有差值间隔为最大公约数,
即设a,b的最大公约数为x,则他们的所有差值只可能为:0,x,2x,3x,...,nx...
并且覆盖一定范围内所有的连续的x的倍数;
基于以上定理及规律,此题解法如下:
只需要判断一下两个区间[yi,zi]和[yj,zj]的差值区间[yj-zi,zj-yi]是否可能存在gcd(xi,xj)的倍数
程序代码:
一部分参考了别人的:
#include <iostream> #include <cstdio> #include <cstring> using namespace std; #define maxn 100001 #define Max(a,b) (a)>(b)?(a):(b) #define Min(a,b) (a)<(b)?(a):(b) struct Node { int x,y,z; }node[1005]; int gcd(int a,int b) { if(b==0) return a; else return gcd(b,a%b); } int check2(int d,int l,int r) { if(l%d==0 || r%d==0) return 1; else if(r/d>l/d) return 1; else return 0; } int check(int i,int j) { if(node[i].y>node[j].z || node[j].y>node[i].z) { if(node[i].y>node[i].z) swap(i,j); int d=gcd(node[i].x,node[j].x); if(check2(d,node[j].y-node[i].z,node[j].z-node[i].y)) return 1; else return 0; } return 1; } int main(){ //freopen("iofile\\input.txt","r",stdin); int n,i,j; while(~scanf("%d",&n)) { for(i=1;i<=n;i++) scanf("%d %d %d",&node[i].x,&node[i].y,&node[i].z); int flag=0; for(i=1;i<=n;i++) { for(j=i+1;j<=n;j++) { if(check(i,j)) {flag=1;break;} } if(flag) break; } if(!flag)printf("Can Take off\n"); else printf("Cannot Take off\n"); } return 0; }