P1516 青蛙的约会和P2421 [NOI2002]荒岛野人

洛谷 P1516 青蛙的约会
算是手推了一次数论题,以前做的都是看题解,虽然这题很水而且还交了5次才过。。。

求解方程(x+amequiv y+an pmod l)中,(a)的最小整数解
(0<x eq yleq 2cdot 10^9,0<n,mleq 2cdot 10^9,0<lleq 2.1cdot 10^9)


做一下变形:

[x-yequiv a(n-m) pmod l ]

(w=x-y,r=n-m),则

[arequiv w pmod l ]

此时,设(gcd(l,r)=gcd),那么可以分两种情况

如果(w)(gcd)的倍数,则原式可以写为:

[ar'gcdequiv w'gcd pmod{l'gcd} ]

根据同余式的性质,可以将(gcd)化简掉,具体证明在这里

[ar'equiv w'pmod l' ]

此时,(gcd(r',l')=1),那么可以求出(r' mod l')的逆元
那么(w'r'^{-1}mod l')即为答案
具体操作就是用 exgcd,求出一个(qr+pl=gcd(r,l)),然后可以表示为(qr'gcd+pl'gcd=gcd)
(gcd)被约掉,就是(qr'+pl'=1),则(q)即为(r'mod l')的逆元

还有一种情况(w)不是(gcd)的倍数,显然无解

再就是注意(w,r)会不会出现负数,如果有负数在取模的时候可能会出现一些玄学情况,要现在(mod l)下把他们变成正的

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline LL read(){
	LL x=0,y=1;
	char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
LL exgcd(LL &x,LL &y,LL a,LL b){
	if(!b) return x=1,y=0,a;
	LL gcd=exgcd(x,y,b,a%b);
	int xx=x;x=y;
	y=xx-a/b*y;
	return gcd;
}
int main(){
	LL n,m,x,y,l;
	x=read();y=read();m=read();n=read();l=read();
	if(m==n) return std::puts("Impossible"),0;
	LL r=n-m,w=x-y;
	if(r<0) r=(r+((-r/l)+1)*l)%l;
	if(w<0) w=(w+((-w/l)+1)*l)%l;
	LL a,b;
	int gcd=exgcd(a,b,r,l);
	if(w%gcd) return std::puts("Impossible"),0;
	l/=gcd;w/=gcd;
	std::printf("%lld",(w*a%l+l)%l);
	return 0;
}

 

洛谷 P2421 [NOI2002]荒岛野人LOJ上也有
这题和上面那个几乎一样,代码复制过去改一改加个主函数就行

克里特岛以野人群居而著称。岛上有排列成环行的(M)个山洞。这些山洞顺时针编号为 (1,2,dots,M)
岛上住着(n)个野人,一开始依次住在山洞 (C_1,C_2,dots,C_n)中。以后每年,第 (i) 个野人会沿顺时针向前走 (P_i)个洞住下来。
每个野人(i)有一个寿命值 (L_i),即生存的年数。

奇怪的是,虽然野人有很多,但没有任何两个野人在有生之年处在同一个山洞中,使得小岛一直保持和平与宁静,这让科学家们很是惊奇。他们想知道,至少有多少个山洞,才能维持岛上的和平呢?数据保证有解,(M) 的值不大于(10^6)

这是真正的数据范围,洛谷上标的很乱:(0<C_i,P_ileq 100,1leq Nleq 15,0leq L_ileq 10^6)


把题意描述成数学语言,就是求一个最小的(M),使得对于任意的(i eq j,c_i+kp_iequiv c_j+kp_j pmod M)无解,或解大于(min(L_i,L_j))
然后这个式子就和上面那个题一模一样了,就是记得判断解的大小就行

一开始想二分处理,然而发现并不满足可二分性,但保证(Mleq 10^6),很小,而且又保证有解,所以可以从(max c_i)开始枚举(M),枚举到成立就行了
复杂度(O(Mn^2log C_i)),超过(10^9)了,而且这还是02年的题,那时候恐怕测评机比现在也慢
不过肯定是跑不满,也不太可能会有什么毒瘤数据,估计是没有更优做法了

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<iomanip>
#include<cstring>
#define reg register
#define EN std::puts("")
#define LL long long
inline int read(){
	register int x=0;register int y=1;
	register char c=std::getchar();
	while(c<'0'||c>'9'){if(c=='-') y=0;c=std::getchar();}
	while(c>='0'&&c<='9'){x=x*10+(c^48);c=std::getchar();}
	return y?x:-x;
}
int exgcd(int &x,int &y,int a,int b){
	if(!b) return x=1,y=0,a;
	LL gcd=exgcd(x,y,b,a%b);
	int xx=x;x=y;
	y=xx-a/b*y;
	return gcd;
}
inline int check(int m,int n,int x,int y,int l,int min){//calc am+x = an+y mod l 
	if(m==n) return 0;
	LL r=n-m,w=x-y;
	if(r<0) r=(r+((-r/l)+1)*l)%l;
	if(w<0) w=(w+((-w/l)+1)*l)%l;
	int a,b;
	int gcd=exgcd(a,b,r,l);
	if(w%gcd) return 0;
	w/=gcd;l/=gcd;
	if((w*a%l+l)%l>min) return 0;
	return 1;
}
int c[19],p[19],l[19];
int main(){
	int n=read(),maxc=0;
	for(reg int i=1;i<=n;i++) c[i]=read(),p[i]=read(),l[i]=read(),maxc=std::max(maxc,c[i]);
	for(reg int m=maxc;;m++){
		for(reg int i=1;i<=n;i++)
			for(reg int j=i+1;j<=n;j++)
				if(check(p[i],p[j],c[i],c[j],m,std::min(l[i],l[j]))) goto FAIL;
		return std::printf("%d",m),0;
		FAIL:;
	}
	return 0;
}
原文地址:https://www.cnblogs.com/suxxsfe/p/12610744.html