题目大意
中国剩余定理"裸题"
这就当是我对(扩展)中国剩余定理的总结吧,还有好多细节的方面的归纳.
中国剩余定理(CRT)
用途就是解出这类问题
我们可以先考虑模数两两互质的情况下的做法.
我们想构造出一个合式使得 x= r_1+r_2+r_3+\cdots +r_n 其中 m_i 可以整除除了第i项的其他项
于是我们可以通过一下做法达到我们的目的:
我们先开一个变量lcm来存它们的最小公倍数(如果两两互质,结果显然是它们的乘积)
然后开一个数组 M_i=\frac{lcm}{m_i} 就是把每项的模数给剔除出来
设 t_i 是 M_it_i\equiv1\ (mod\ m_i)的一个解.
于是答案就是 ans=\displaystyle\sum_{i=1}^na_iM_it_i
通解就是 ans+klcm 其中k是整数
扩展中国剩余定理(exCRT)
这才是大多数题目真正会用到的算法,因为它不必要求模数两两互质,我们还是可以通过刚刚的想法来做
假设前 k-1 个方程的解是 x , 前 k-1 的lcm还是用 lcm 表示
则前 k-1 个方程的通解是 x+im 其中i是整数
现在求第 k 个方程,我们需要一个整数t,满足 x+tm\equiv a_k\ (mod\ m_k)
等价于 tm\equiv a_k-x\ (mod\ m_k)
答案更新为 x=x+tm
回到这道题上来
不难发现,这道题其实就是要求如下的线性同余方程组
\begin{cases}x\equiv a_1\times ATK_1^{-1}\ (mod\ p_1)\x\equiv a_2\times ATK_2^{-2}\ (mod\ p_2)\ \cdots\ x\equiv a_n\times ATK_n^{-n}\ (mod\ p_n) \end{cases}
至于每一项的ATK,可以用multiset处理出来(想快一点手写平衡树)
但是我们发现,如果ATK不与模数互质的话,它的逆元是不好求的
同余的消去律:
如果 ab\equiv ac\ (mod\ n),而 d=gcd(a,n) ,那么a可以在同余式两边消去
b\equiv c\ (mod\ \frac{n}{d})
特别是当a和n互质,ab\equiv ac\ (mod\ n) 可推到 b \equiv c\ (mod\ n)
根据模运算的消去律,我们可以去掉最大公因数 gcd(ATK_i*lcm,p_i) 如果左式无法整除,则无解
代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 |
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int MAXN=100005; ll a[MAXN],p[MAXN],bone[MAXN],atk[MAXN],ans; int n,m; ll read(){ ll x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();} return x*f; } ll mul(ll a,ll b,ll mod){ a = (a % mod + mod) % mod;//可能为负数,不加60分 b = (b % mod + mod) % mod; ll ans=0; while(b){ if(b&1)ans=(ans+a)%mod; a=(a+a)%mod; b>>=1; } return ans; } ll exgcd(ll a,ll b,ll &x,ll &y){ if(b==0){x=1,y=0;return a;} ll d=exgcd(b,a%b,x,y); ll z=x;x=y;y=z-y*(a/b); return d; } ll gcd(ll a,ll b){ return b?gcd(b,a%b):a; } ll inverse(ll a, ll mod) { ll x, y; exgcd(a, mod, x, y); return (x % mod + mod) % mod; } ll excrt(){ ans=0; ll lcm=1; for(int i=1;i<=n;i++){ ll ax=mul(atk[i],lcm,p[i]); ll bx=a[i]-mul(atk[i],ans,p[i]); ll d=gcd(ax,p[i]); if(bx%d!=0){ ans=-1; return 0; } ll x=mul(bx/d,inverse(ax/d,p[i]/d),p[i]/d); ans+=lcm*x,lcm*=p[i]/d; } ans=(ans%lcm+lcm)%lcm; return lcm; } int main(){ int t=read(); while(t--){ multiset<ll>bst;ans=0; n=read(),m=read(); for(int i=1;i<=n;i++)a[i]=read(); for(int i=1;i<=n;i++)p[i]=read(); for(int i=1;i<=n;i++)bone[i]=read(); for(int i=1;i<=m;i++){ll x=read();bst.insert(x);} ll mx=0; for(int i=1;i<=n;i++){ multiset<ll>::iterator it = bst.upper_bound(a[i]); if (it != bst.begin()) { it--; } atk[i] = (*it), bst.erase(it), bst.insert(bone[i]); mx = max(mx, (a[i] + atk[i] - 1) / atk[i]); } ll lcm=excrt();ll x=ans; if(x!=-1&&x<mx){ x+=lcm*((mx-x+lcm-1)/lcm); } cout<<x<<endl; } return 0; } |