数据结构与算法:逆元、除法同余和容斥原理
前言
马上就要到线段树了,有点紧张()
一、逆元和除法同余
在一些组合数学的题目里,往往需要求解组合数。而又因为一阶乘数据会很大,所以往往这类题需要进行取模操作。而因为公式中存在除法,除法同余又和加减乘同余都不一样,此时就需要用到逆元的知识来求解除法同余了。
组合数学的题永远做不出来呜呜呜……
1.逆元
如果想要求解a除以b取模,首先必须保证a除以b可以整除,其次需要保证模的数MOD为一个质数,最后还需要保证b和MOD的最大公约数为1,以上这三点往往题目本身就会保证,不需要做题时再考虑了。
之后,若想求解(10/5)%3,因为10除以5和10乘以1/5结果一致,所以抽象一点说逆元的思路就是找一个数代替1/5和10去乘。直接说结论,b的逆元就是b的MOD-2次方中间每步取模的结果。所以在求解的过程中还需要用到乘法快速幂。
2.除法同余
求出逆元后,a除以b再取模的除法同余就可以转化成用a乘以b的逆元再取模。
#include <bits/stdc++.h>
using namespace std;typedef long long ll;
typedef pair<int,int> pii;const int MOD=41;//质数//验证方法
int solve1(ll a,ll b)
{return (a/b)%MOD;
}//乘法快速幂
ll power(ll x,int n)
{ll ans=1;while(n>0){if((n&1)!=0){ans=(ans*x)%MOD;}x=(x*x)%MOD;n>>=1;}return ans;
}int solve2(ll a,ll b)
{ll inv=power(b,MOD-2);return (a*inv)%MOD;
}void test()
{cout<<"Start"<<endl;ll b=3671613;ll a=67312*b;cout<<solve1(a,b)<<endl;cout<<solve2(a,b)<<endl;cout<<"Over"<<endl;
}int main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t=1;//cin>>t;// while(t--)// {// solve(); // }test();return 0;
}
可以得到答案均为31,但若MOD为一个合数,答案就会出现错误。
3.逆元的线性递推——模意义下的乘法逆元
#include <bits/stdc++.h>
using namespace std;typedef long long ll;
typedef pair<int,int> pii;void solve()
{int n,p;scanf("%d%d",&n,&p);//inv[i]:数字i的逆元//inv[1]=1//inv[i]=(p-inv[p%i]*(p/i)%p)//逆元vector<ll>inv(n+1);inv[1]=1;for(int i=2;i<=n;i++){inv[i]=(p-inv[p%i]*(p/i)%p);}for(int i=1;i<=n;i++){printf("%d\n",inv[i]);}
}int main()
{ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int t=1;//cin>>t;while(t--){solve(); }return 0;
}
线性递推的这个公式没啥好说的,背过就行。
注意,这道题卡常卡的特别严重,必须用scanf和printf才能通过。
4.逆元的阶乘递推
#include <bits/stdc++.h>
using namespace std;