设走了t步,则第一只青蛙跳了,第二只是
则有
那么有
所以可以列出不定方程 其中t和k为未知数。
用拓展欧几里得求解。
求完之后判断一下有没有解
然后x乘上K /gcd就是真正的解
然后b /gcd可以作为模,把x调整成最小正整数解
最后注意开long long,数论题大多都要开long long
#include
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;
typedef long long ll;
void exgcd(ll a, ll b, ll& d, ll& x, ll& y)
{
if(!b) { d = a; x = 1; y = 0; }
else { exgcd(b, a % b, d, y, x); y -= x * (a / b); }
}
int main()
{
ll xx, yy, n, m, L;
scanf("%lld%lld%lld%lld%lld", &xx, &yy, &m, &n, &L);
ll A, B, K, d, x, y;
A = m - n, B = -L, K = yy - xx;
exgcd(A, B, d, x, y);
if(K % d != 0) { puts("Impossible"); return 0; }
x *= K / d; ll MOD = B / d;
printf("%lld\n", (x % MOD + MOD) % MOD);
return 0;
}
转化成即可
#include
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;
typedef long long ll;
void exgcd(ll a, ll b, ll& d, ll& x, ll& y)
{
if(!b) { d = a; x = 1; y = 0; }
else { exgcd(b, a % b, d, y, x); y -= x * (a / b); }
}
int main()
{
ll a, b, d, x, y;
scanf("%lld%lld", &a, &b);
exgcd(a, b, d, x, y);
ll MOD = b / d;
printf("%lld\n", (x % MOD + MOD) % MOD);
return 0;
}
这是一道非常棒的题目,考到了很多的知识点
(1)质因数分解,快速幂模板熟练
(2)如果对a^b进行质因数分解,那么a照常分解,最后c[i],也就是质因数的幂乘以b就好了
(3) 这道题用到了逆元。逆元很类似倒数这个概念,只不过是在modp的条件下。
逆元可以干嘛呢?当遇到a/b时,可以把除以b变成乘以b的逆元(一般加减乘都可以直接模,除就用逆元),但是b和p要互质,当p为质数的时候,等价于b不是p的倍数,即b%p!=0
如果等于的话可以特判一下。用b^(p-2)求逆元
#include
#define cal(a, b) a = (a * b) % MOD
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;
typedef long long ll;
const int MOD = 9901;
ll P[20], c[20], m;
void divide(ll a)
{
for(int i = 2; (ll)i * i <= a; i++)
if(a % i == 0)
{
P[m] = i;
while(a % i == 0)
{
a /= i;
c[m]++;
}
m++;
}
if(a > 1) P[m] = a, c[m++] = 1;
}
ll power(ll a, ll b)
{
ll res = 1 % MOD; a %= MOD;
for(; b; b >>= 1)
{
if(b & 1) cal(res, a);
cal(a, a);
}
return res;
}
int main()
{
ll a, b;
scanf("%lld%lld", &a, &b);
divide(a);
ll ans = 1;
REP(i, 0, m)
{
ll p = P[i], n = c[i] * b + 1;
if((p - 1) % MOD == 0)
{
ans = n % MOD * ans % MOD;
continue;
}
ll t = power(p, n);
t = (t - 1 + MOD) % MOD;
cal(ans, t);
cal(ans, power(p - 1, MOD - 2));
}
printf("%lld\n", ans);
return 0;
}
顺便补充一下线性求逆的代码
void get_inv()
{
inv[1] = 1;
REP(i, 2, MAXN)
inv[i] = (p - p / i) * inv[p % i] % p;
}
中国剩余定理还是比较好用的,代码量很小,记住套路就好了。
#include
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;
typedef long long ll;
const int MAXN = 15;
ll m[MAXN], a[MAXN];
void exgcd(ll a, ll b, ll& d, ll& x, ll& y)
{
if(!b) { d = a; x = 1; y = 0; return; }
else { exgcd(b, a % b, d, y, x); y -= x * (a / b); }
}
int main()
{
int n;
scanf("%d", &n);
ll M = 1;
REP(i, 0, n)
{
scanf("%lld%lld", &m[i], &a[i]);
M *= m[i];
}
ll ans = 0, d, x, y;
REP(i, 0, n)
{
ll mi = M / m[i];
exgcd(mi, m[i], d, x, y);
ans = (ans + x * mi * a[i]) % M;
}
ans = (ans % M + M) % M;
printf("%lld\n", ans);
return 0;
}
同余方程解法
#include
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;
typedef long long ll;
void exgcd(ll a, ll b, ll& d, ll& x, ll& y)
{
if(!b) { d = a; x = 1; y = 0; return; }
else { exgcd(b, a % b, d, y, x); y -= x * (a / b); }
}
int main()
{
ll n, b1, m1, b2, m2;
scanf("%lld%lld%lld", &n, &m1, &b1);
REP(i, 1, n)
{
scanf("%lld%lld", &m2, &b2);
ll A, B, K, x, y, d;
A = m1, B = m2, K = b2 - b1;
exgcd(A, B, d, x, y);
x *= K / d; int mod = B / d;
x = (x % mod + mod) % mod;
b1 = m1 * x + b1;
m1 = m1 / d * m2;
}
printf("%lld\n", b1);
return 0;
}
和上一题没多大区别
#include
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;
typedef long long ll;
void exgcd(ll a, ll b, ll& d, ll& x, ll& y)
{
if(!b) { d = a; x = 1; y = 0; return; }
else { exgcd(b, a % b, d, y, x); y -= x * (a / b); }
}
int main()
{
ll n, b1, m1, b2, m2;
while(~scanf("%lld", &n))
{
scanf("%lld%lld", &m1, &b1);
bool ok = true;
REP(i, 1, n)
{
scanf("%lld%lld", &m2, &b2);
ll A, B, K, x, y, d;
A = m1, B = m2, K = b2 - b1;
exgcd(A, B, d, x, y);
if(K % d != 0) ok = false;
x *= K / d; int mod = B / d;
x = (x % mod + mod) % mod;
b1 = m1 * x + b1;
m1 = m1 / d * m2;
}
if(!ok) puts("-1");
else printf("%lld\n", b1);
}
return 0;
}
同样,中国剩余定理我都用同余方程做……
比较奇怪的是,第一个模数如果是正的话就会WA掉18分
要写为负。
我也不知道为什么
#include
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;
void exgcd(int a, int b, int& d, int& x, int& y)
{
if(!b) { d = a; x = 1; y = 0; return; }
else { exgcd(b, a % b, d, y, x); y -= x * (a / b); }
}
int main()
{
int p, e, i, D, kase = 0;
while(~scanf("%d%d%d%d", &p, &e, &i, &D))
{
if(p == -1) break;
int A, B, K, d, x, y;
A = 23, B = -28, K = e - p;
exgcd(A, B, d, x, y);
x *= (K / d); int mod = B / d;
x = (x % mod + mod) % mod;
int b = 23 * x + p;
int m = 23 * 28;
A = m, B = 33, K = i - b;
exgcd(A, B, d, x, y);
x *= (K / d); mod = B / d;
x = (x % mod + mod) % mod;
b = m * x + b;
int ans = b - D <= 0 ? b - D + 21252 : b - D;
printf("Case %d: the next triple peak occurs in %d days.\n", ++kase, ans);
}
return 0;
}
这道题写了好久……
学了一个新的算法,高次同余方程的解法。
可以参考这位大牛的写法大牛博客
注意几个点
(1)输入的时候可以直接将a取模。
(2)注意什么时候不成立。对于高次同余方程a^x = b (mod p)
将a和b对p取模之后,如果a为0,b为0,则x = 0, 如果a 为 0, b不为0,则无解
(3)BSGS算法中m是向上取整,即m = ceil(sqrt(p))
#include
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;
typedef long long ll;
ll a, b, p;
ll power(ll a, ll b, ll p)
{
ll res = 1 % p; a %= p;
for(; b; b >>= 1)
{
if(b & 1) res = res * a % p;
a = a * a % p;
}
return res;
}
void solve1()
{
printf("%lld\n", power(a, b, p));
}
ll gcd(ll a, ll b) { return !b ? a : gcd(b, a % b); }
inline ll inv(ll a) { return power(a, p - 2, p); }
void solve2()
{
if(gcd(a, p) != 1) puts("Orz, I cannot find x!");
else printf("%lld\n", b * inv(a) % p);
}
map mp;
void solve3()
{
mp.clear();
b %= p; a %= p;
if(a == 0 && b == 0) { puts("1"); return; }
if(a == 0 && b != 0) { puts("Orz, I cannot find x!"); return; }
ll m = ceil(sqrt(p)); //注意这里向上取整
mp[b] = m + 1;
ll sum = 1, t = inv(a);
REP(i, 1, m)
{
sum = sum * t % p;
ll q = b * sum % p;
if(!mp[q]) mp[q] = i;
}
t = power(a, m, p); sum = 1;
REP(i, 0, m)
{
if(mp[sum])
{
if(mp[sum] == m + 1) printf("%lld\n", i * m);
else printf("%lld\n", i * m + mp[sum]);
return;
}
sum = sum * t % p;
}
puts("Orz, I cannot find x!");
}
int main()
{
int T, k;
while(~scanf("%d%d", &T, &k))
{
while(T--)
{
scanf("%lld%lld%lld", &a, &b, &p);
if(k == 1) solve1();
if(k == 2) solve2();
if(k == 3) solve3();
}
}
return 0;
}
令
则
可以推出
exgcd解方程就好了。
注意真正实现的时候y的系数是n,不是-n
不然x最后输出可能是负的。
#include
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;
typedef long long ll;
void exgcd(ll a, ll b, ll& d, ll& x, ll& y)
{
if(!b) { d = a; x = 1; y = 0; return; }
else { exgcd(b, a % b, d, y, x); y -= x * (a / b); }
}
int main()
{
int T;
scanf("%d", &T);
while(T--)
{
ll p, q, x, y, d, n, D;
scanf("%lld%lld%lld%lld", &n, &D, &p, &q);
ll A = D, B = n, K = q - p;
exgcd(A, B, d, x, y);
if(K % d != 0) { puts("Impossible"); continue; }
x *= K / d; ll mod = B / d;
x = (x % mod + mod) % mod;
printf("%lld\n", x);
}
return 0;
}
和上一题差不多
#include
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;
typedef long long ll;
ll p[40], x, y, d, a, b, c, k;
void exgcd(ll a, ll b, ll& d, ll& x, ll& y)
{
if(!b) { d = a; x = 1; y = 0; return; }
else { exgcd(b, a % b, d, y, x); y -= x * (a / b); }
}
int main()
{
p[0] = 1;
_for(i, 1, 32) p[i] = p[i-1] << 1;
while(~scanf("%lld%lld%lld%lld", &a, &b, &c, &k))
{
if(a == 0 && b == 0 && c == 0 && k == 0) break;
ll A = c, B = p[k], K = b - a;
exgcd(A, B, d, x, y);
if(K % d != 0) { puts("FOREVER"); continue; }
x *= K / d; ll mod = B / d;
x = (x % mod + mod) % mod;
printf("%lld\n", x);
}
return 0;
}
我竟然做出了noi的题,感动!!(虽然WA了几次)
我第一眼以为是二分洞穴的数目来做。
设当前洞穴数为p,对于当前两个人来说
则
化简得
解方程就好了,但这里有个很重要的细节。
即使B为正,如果A为负或者K为负会导致gcd为负,最终算出来的x可能为负
所以最后加上一句 if(x < 0) x += abs(t);
x *= K / d; ll t = B / d;
x = (x % t + t) % t;
if(x < 0) x += abs(t);
如果无解,当前p就成立。当x大于l1或者大于l2时p也成立(还没走到一起就有人死了)
因为看到n<=15,所以果断暴力判断每两个人在当前洞穴数下
会不会到一个洞穴。
然后交上去WA,30分
然后我想了想,貌似不满足单调性……(其实可以打表出来看一下)
那我就非常非常暴力地枚举p吧,从1到1e6
交上去
哇靠90分,竟然不会超时??
有一个点WA
然后发现p最小是cmax,不是0
然后交了
AC!!!!
#include
#define REP(i, a, b) for(register int i = (a); i < (b); i++)
#define _for(i, a, b) for(register int i = (a); i <= (b); i++)
using namespace std;
const int MAXN = 20;
typedef long long ll;
ll c[MAXN], p[MAXN], l[MAXN];
int n;
void exgcd(ll a, ll b, ll& d, ll& x, ll& y)
{
if(!b) { d = a; x = 1; y = 0; return; }
else { exgcd(b, a % b, d, y, x); y -= x * (a / b); }
}
bool judge(int i, int j, ll mod)
{
ll A = p[i] - p[j], B = mod, K = c[j] - c[i], d, x, y;
exgcd(A, B, d, x, y);
if(K % d != 0) return true;
x *= K / d; ll t = B / d;
x = (x % t + t) % t;
if(x < 0) x += abs(t);
return x > l[i] || x > l[j];
}
bool check(ll mod)
{
REP(i, 0, n)
REP(j, i + 1, n)
if(!judge(i, j, mod))
return false;
return true;
}
int main()
{
ll maxt = 0;
scanf("%d", &n);
REP(i, 0, n)
{
scanf("%lld%lld%lld", &c[i], &p[i], &l[i]);
maxt = max(maxt, c[i]);
}
ll ans = maxt;
while(ans <= 1e6)
{
if(check(ans)) break;
ans++;
}
printf("%lld\n", ans);
return 0;
}