最近也是在备战比赛,所以也是来小小的复习了一下以前学的东西
最重要的是第一道题!
最重要的是第一道题!
最重要的是第一道题!
先放拓欧板子(不懂怎么推出了就发在评论区或者私聊)
int exgcd(int a,int b,int &x,int &y)
{if(b==0){x=1,y=0;return a;}int x2,y2;int d=exgcd(b,a%b,x2,y2)x=y2;y=x2-a/b*y2;return d;
}
例题:
P2054 [AHOI2005] 洗牌
我们先来通过6张牌来列举一下
初始 [ 1 , 2 , 3 , 4 , 5 , 6 ]
第一次 [ 4 , 1 , 5 , 2 , 6 , 3 ]
第二次 [ 2 , 4 , 6 , 1 , 3 , 5 ]
第三次 [ 1 , 2 , 3 , 4 , 5 , 6 ]
我们通过上面对1这个数据分析,
初始值在1位置,第一次变换后在2位置,第二次变换后在4位置,第三次变换后在1位置
我们发现每次都相当于乘2,但是为什么会被突然折断呢?我们不难想到可能是存在取模操作
因此我们可以大胆猜想这个数是7,也就是(n+1)
我们再对3这个数据进行分析
初始值在3位置,第一次在6位置,第二次再5位置,第三次在3位置
我们发现也是每次位置都乘2,然后取模7(n+1)
因此我们就发现了规律
第i个数m次操作后的位置为 :( i* 2^m)%(n+1)
但是题意问的是m次操作之后,谁在L这个位置,不是问你每个数在哪,而是问你在特定位置的数,很明显,我们一开始想的是遍历一遍所有数,找到谁在那个位置,但是数据是10^10,很明显会超时,因此我们只能去倒推一遍 这个式子
假设x在L那个位置,那么
(2^m)*x= (n+1) *k + L
请问你看这个式子想到了什么???
我嘞个豆,啥也没想到,你是要毁了我吗?孩子
那我再给你将这个式子变一下形式
(2^m)*x + (n +1) *y = L 这下能看出来拓展欧几里得的形式了吧
为什么能用拓欧呢?因为题目上说了,n是一个偶数,n+1一定是个奇数,2^m次方一定是个偶数,所以肯定互质的
但是这个式子也很麻烦,能不能再变一下呢?那肯定是可以的
我们先去求
(2^m)*x + (n +1) *y = 1,然后最后给x乘以L不就是了,说罢,我的气息终于不再掩饰,顷刻将代码炼化出来
77pts
#include <bits/stdc++.h>
using namespace std;
#define int long long int n, m, l;
int mod; int fast(int a, int b, int mod)
{ int result = 1; int base = a % mod; while (b > 0) { if (b % 2 == 1) { result = (result * base) % mod; } base = (base * base) % mod; b /= 2; } return result;
} int exgcd(int a, int b, int &x, int &y)
{ if (b == 0) { x = 1; y = 0; return a; } int d = exgcd(b, a % b, x, y); int x2 = x; x = y; y = x2 - (a / b) * y;return d;
} signed main() { ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> n >> m >> l; mod = n + 1; int x, y; int f = fast(2, m, mod);int z = exgcd(f, mod, x, y); x = (x+mod) % mod; cout << (x*l) % mod << '\n';
}
没想到被暗算了,在运算过程中有可能会导致超出long long的范围,因此需要用到龟速幂,也就是在乘法的过程中用快速乘去运算
84pts
#include <bits/stdc++.h>
using namespace std;
#define int long long int n, m, l;
int mod; int mul(int a,int b)
{int result=0;while(b>0){if(b%2==1){result=(result+a)%mod;}a=(a+a)%mod;b/=2;}return result%mod;
}int fast(int a, int b)
{ int result = 1; int base = a % mod; while (b > 0) { if (b % 2 == 1) { result=mul(result,base);} base = mul(base,base); b /= 2; } return result%mod;
} int exgcd(int a, int b, int &x, int &y)
{ if (b == 0) { x = 1; y = 0; return a; } int d = exgcd(b, a % b, x, y); int x2 = x; x = y; y = x2 - (a / b) * y;return d;
} signed main() { ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> n >> m >> l; mod = n + 1; int x, y; int f = fast(2, m);int z = exgcd(f, mod, x, y); x = (x+mod) % mod; cout << (x*l) % mod << '\n';
}
为什么还是错一个点嘞?左思右想都没想明白,
才发现,在逆元前面还是有可能会爆数据,为什么不能用乘法逆元的积性函数的性质,我先去求2和n+1的逆元然后再去m次方后再去乘以L,思考便后,我便不再掩饰自己的气息,仿佛有成尊之势
果然AC了
100pts
#include <bits/stdc++.h>
using namespace std;
#define int long long int n, m, l;
int mod; int mul(int a,int b)
{int result=0;while(b>0){if(b%2==1){result=(result+a)%mod;}a=(a+a)%mod;b/=2;}return result%mod;
}int fast(int a, int b)
{ int result = 1; int base = a % mod; while (b > 0) { if (b % 2 == 1) { result=mul(result,base);} base = mul(base,base); b /= 2; } return result%mod;
} int exgcd(int a,int b,int &x,int &y)
{if(b==0){x=1,y=0;return a;}int x2,y2;int d=exgcd(b,a%b,x2,y2);x=y2;y=x2-a/b*y2;return d;
}signed main()
{ ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin >> n >> m >> l; mod = n + 1; int x, y; exgcd(2, mod, x, y); x = (x+mod) % mod; x=fast(x,m);cout<<mul(x,l) << '\n';
}
P5656 【模板】二元一次不定方程 (exgcd)
思路:题目不是很难,但是处理的条件颇多,只要有耐心并且细心一点都可以写出来
#include<bits/stdc++.h>
using namespace std;
#define int long long
int t;
int a,b,c;
int gcd(int a,int b)
{return b==0?a:gcd(b,a%b);
}
int exgcd(int a,int b,int &x,int &y)
{if(b==0){x=1,y=0;return a;}int x2,y2;int d=exgcd(b,a%b,x2,y2);x=y2;y=x2-a/b*y2;return d;
}
signed main()
{cin>>t;while(t--){int x,y;int d;int minnx,minny,maxnx,maxny;int cnt=0;cin>>a>>b>>c;d=gcd(a,b);if(c%d!=0){cout<<-1<<"\n";}else{a/=d,b/=d,c/=d;exgcd(a,b,x,y);x*=c,y*=c;if(x>0&&x%b!=0){minnx=x%b;}else{minnx=x%b+b;}maxny=(c-minnx*a)/b;if(y>0&&y%a!=0){minny=y%a;}else{minny=y%a+a;}maxnx=(c-minny*b)/a; if(maxnx>0){cnt=(maxnx-minnx)/b+1;}if(cnt==0){cout<<minnx<<" "<<minny<<"\n"; }else{cout<<cnt<<" "<<minnx<<" "<<minny<<" "<<maxnx<<" "<<maxny<<"\n";}}}return 0;
}
P1516 青蛙的约会
思路:这题我们可以写出一个方程
假设第一只青蛙的起始位置为n,速度为a,第二只青蛙的起始位置为m,速度为b
可以写出方程
(a-b)*t+L *y=m-n
想到了什么?当然是拓欧
直接去写拓欧去求出最小的x,然后x*(m-n)/gcd(a-b,L)即可,当然不要忘记取模L
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,m,a,b,l;
int gcd(int a,int b)
{return b==0?a:gcd(b,a%b);
}int exgcd(int a,int b,int &x,int &y)
{if(b==0){x=1,y=0;return a;}int x2,y2;int d=exgcd(b,a%b,x2,y2);x=y2;y=x2-a/b*y2;return d;
}signed main()
{int x,y;cin>>n>>m>>a>>b>>l;if(a<b){swap(a,b),swap(n,m);}a=a-b;b=l;int d=gcd(a,b);int c=(m-n+l)%l;exgcd(a,b,x,y);if(c%d!=0){cout<<"Impossible\n";return 0;}l/=d;cout<<(c/d*x%l+l)%l;return 0;
}
P3951 [NOIP2017 提高组] 小凯的疑惑
思路:其实就是看自己的思维的,如果想要能够被表示那么就说明x和y都要大于0,所以不能表示的最小数一定为
-a+?*b(?表示现在还不知道)
那为什么一定为这种形式呢?
x不能等于-2吗?
肯定可以啊,只不过一定不是最大,因为还有一个x=-1的时候更大
那么?的范围是什么,最大是(a-1)
为什么呢?假如可以为a的话,那么方程就可以化简为a*(b-1),那么就可以用a类型钞票单独表示了
最后就可以写出-a+b*(a-1)
也就是a*b-a-b;
#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main()
{int a,b;cin>>a>>b;cout<<(a*b-a-b);return 0;
}