写题解看成复习笔记
(这样就能够少写一篇博客了 Yeah)函数
> Link LOJ 2721ui
「问题」spa
求解线性同余方程组code
$$ \begin{cases} x\equiv a_1\pmod{m_1}\\ x\equiv a_2\pmod{m_2}\\ \cdots\\ x\equiv a_n\pmod{m_n} \end{cases} $$ get
$m$ 能够不互质。input
考虑等价地合并两个同余方程:博客
能够把同余方程写为不定方程的形式,因而获得参数的一些关系:string
因为 \(m_1,m_2\) 不必定互质,因此关于 \(k_1,k_2\) 的不定方程不必定有解。具体地说,设 \((m_1,m_2)=g\),则it
等式左侧 \(g\mid k_1m_1-k_2m_2\),则右侧也必须知足 \(g\mid a_2-a_1\),不然无解。io
若知足上述条件,则等式两边同时除以 \(g\) 仍然等价,即
此时 \((m_1',m_2')=1\),能够直接用 exGCD 解决上述问题,获得 \(k_1\) 的一个特解为 \(k_0\)。由
能够知道 \(k_1\) 的通解为 \(k_1=k_0+nm_2'(n\in\mathbb{Z})\)。
将 \(k_1\) 代回 \(x\) 的表达式,则有
因而就获得了两个式子合并后的等价的式子。这样不断合并就能够获得最终 \(x\) 的通解。
一开始能够根据输入求出「用哪一把剑攻击第 \(i\) 条龙」,记为 \(w_i\)。具体能够用 multiset 实现。
multiset 内部每一个位置储存了一个元素(并非把相同的元素合到同一个位置而且记录次数),所以用迭代器 iterator 访问其中的某个位置,访问到的是单个元素。若是按迭代器顺序访问 multiset,就至关于把插入的全部元素排了个序。
multiset 内置有 lower_bound 和 upper_bound 函数,前者返回第一个大于等于给定值的元素的迭代器,后者返回第一个严格大于给定值的元素的迭代器。
multiset 的 delete 函数有两类参数。第一类是给定数值,会删除其中全部该数值;第二类是给定迭代器,会删除迭代器对应的元素——这样就只会删掉一个数字。
利用这些特色,咱们能够用 upper_bound 找到第一把攻击力大于龙的生命值的剑,而上一把剑就是攻击力小于等于生命值的剑。而后用 delete 删除该剑的迭代器(不能是数值!)。
记龙的生命为 \(h_i\),回复力为 \(r_i\),则有两个限制:
问题直接转化为 \(n\) 对方程构成的方程组
不等式方程能够求出 \(x\) 的下界。考虑如何求解同余方程。
\(w_i,r_i\) 不必定互质,因而可能自己就无解。记 \((w_i,r_i)=g_i\),则必须知足 \(g_i\mid h_i\),方程才有解。
知足有解的条件后,\(w_i,h_i,r_i\) 同时除以 \(g_i\),获得等价的方程 \(w_i'x=h_i'\pmod {r_i'}\),此时 \((w_i',r_i')=1\),\(w_i'\) 存在逆元,因而能够获得
这个方程就能够用 exCRT 了。
/*Lucky_Glass*/ #include<set> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; template<class T>T rin(T &r){ int b=1,c=getchar();r=0; while(c<'0' || '9'<c) b=c=='-'?-1:b,c=getchar(); while('0'<=c && c<='9') r=(r<<1)+(r<<3)+(c^'0'),c=getchar(); return r*=b; } typedef long long llong; const int N=1e5+10; #define con(type) const type & multiset<llong> sword; int n,m; llong ih[N],rec[N],rew[N],atk[N]; llong ina_GCD(con(llong)a,con(llong)b){return b?ina_GCD(b,a%b):a;} llong ex_GCD(con(llong)a,con(llong)b,llong &x,llong &y){ if(!b){x=1,y=0;return a;} llong ret=ex_GCD(b,a%b,x,y); swap(x,y); y-=a/b*x; return ret; } llong ina_ABS(con(llong)a){return a<0?-a:a;} llong mul(llong a,llong b,llong mod){ bool neg=(a<0)^(b<0); a=ina_ABS(a),b=ina_ABS(b); a%=mod,b%=mod; llong ret=0; while(b){ if(b&1) ret=(ret+a)>=mod? ret+a-mod:ret+a; a<<=1,b>>=1; if(a>=mod) a-=mod; } if(neg && ret) return mod-ret; return ret; } pair<llong,llong> comb_CRT(con(llong)m0,con(llong)r0,con(llong)m1,con(llong)r1){ llong g=ina_GCD(m0,m1); if((r1-r0)%g){ // printf("(%lld,%lld)=%lld %lld %lld\n",m0,m1,g,r1,r0); return make_pair(-1ll,-1ll); } llong p,q; ex_GCD(m0/g,m1/g,p,q); p=mul((r1-r0)/g,p,m1/g); llong m2=m0/g*m1,r2=(mul(p,m0,m2)+r0)%m2; if(r2<0) r2+=m2; return make_pair(m2,r2); } llong solve(){ sword.clear(); rin(n),rin(m); for(int i=1;i<=n;i++) rin(ih[i]); for(int i=1;i<=n;i++) rin(rec[i]); for(int i=1;i<=n;i++) rin(rew[i]); for(int i=1,tmp;i<=m;i++) sword.insert(rin(tmp)); for(int i=1;i<=n;i++){ multiset<llong>::iterator it=sword.upper_bound(ih[i]); if(it!=sword.begin()) it--; atk[i]=*it; sword.erase(it); sword.insert(rew[i]); } llong mnbon=0; pair<llong,llong> now(-1ll,-1ll); for(int i=1;i<=n;i++){ mnbon=max(mnbon,(ih[i]+atk[i]-1)/atk[i]); llong k=atk[i],r=ih[i],m=rec[i]; // printf("%lld x = %lld (mod %lld) -> ",k,r,m); //kx = r (mod m) k%=m,r%=m; if(!k){ if(r){ // printf("A\n"); return -1ll; } continue; } llong g=ina_GCD(k,m); if(r%g){ // printf("B\n"); return -1ll; } k/=g,m/=g,r/=g; llong invk,non; ex_GCD(k,m,invk,non); invk=(invk%m+m)%m; r=mul(r,invk,m); // printf("x = %lld (mod %lld)\n",r,m); pair<llong,llong> tmp(m,r); if(i==1) now=tmp; else now=comb_CRT(now.first,now.second,tmp.first,tmp.second); if(now.first==-1){ // printf("C\n"); return -1ll; } } if(now.first==-1) return mnbon; if(now.second>=mnbon) return now.second; else{ llong k=(mnbon-now.second+now.first-1)/now.first; return now.second+k*now.first; } } int main(){ // freopen("input.in","r",stdin); freopen("dragon.in","r",stdin); freopen("dragon.out","w",stdout); int cas;rin(cas); while(cas--) printf("%lld\n",solve()); return 0; }