寫題解當作復習筆記
(這樣就可以少寫一篇博客了 Yeah)
# 題面
> Link LOJ 2721
# exCRT
「問題」
求解線性同余方程組
$$ \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} $$
$m$ 可以不互質,
考慮等價地合并兩個同余方程:
\[\begin{cases} x\equiv a_1\pmod{m_1}\\ x\equiv a_2\pmod{m_2} \end{cases} \]可以把同余方程寫為不定方程的形式,于是得到引數的一些關系:
\[x=k_1m_1+a_1=k_2m_2+a_2 \]由于 \(m_1,m_2\) 不一定互質,所以關于 \(k_1,k_2\) 的不定方程不一定有解,具體地說,設 \((m_1,m_2)=g\),則
\[k_1m_1-k_2m_2=a_2-a_1 \]等式左側 \(g\mid k_1m_1-k_2m_2\),則右側也必須滿足 \(g\mid a_2-a_1\),否則無解,
若滿足上述條件,則等式兩邊同時除以 \(g\) 仍然等價,即
\[\begin{array}{c} m_1'=\tfrac{m_1}{g},m_2'=\tfrac{m_2}{g},t=\tfrac{a_2-a_1}{g}\\ k_1m_1'-k_2m_2'=t \end{array} \]此時 \((m_1',m_2')=1\),可以直接用 exGCD 解決上述問題,得到 \(k_1\) 的一個特解為 \(k_0\),由
\[k_1m_1'-k_2m_2'=(k_1+m_2')m_1'-(k_2+m_1')m_2' \]可以知道 \(k_1\) 的通解為 \(k_1=k_0+nm_2'(n\in\mathbb{Z})\),
將 \(k_1\) 代回 \(x\) 的運算式,則有
\[\begin{array}{c} x=k_0m_1+a_1+nm_1m_2'\\ x\equiv k_0m_1+a_1\pmod{m_1m_2'} \end{array} \]于是就得到了兩個式子合并后的等價的式子,這樣不斷合并就可以得到最終 \(x\) 的通解,
# 決議
一開始可以根據輸入求出「用哪一把劍攻擊第 \(i\) 條龍」,記為 \(w_i\),具體可以用 multiset 實作,
點擊展開/折疊multiset使用技巧
multiset 內部每個位置儲存了一個元素(并不是把相同的元素合到同一個位置并且記錄次數),因此用迭代器 iterator 訪問其中的某個位置,訪問到的是單個元素,如果按迭代器順序訪問 multiset,就相當于把插入的所有元素排了個序,
multiset 內置有 lower_bound 和 upper_bound 函式,前者回傳第一個大于等于給定值的元素的迭代器,后者回傳第一個嚴格大于給定值的元素的迭代器,
multiset 的 delete 函式有兩類引數,第一類是給定數值,會洗掉其中所有該數值;第二類是給定迭代器,會洗掉迭代器對應的元素——這樣就只會刪掉一個數字,
利用這些特點,我們可以用 upper_bound 找到第一把攻擊力大于龍的生命值的劍,而上一把劍就是攻擊力小于等于生命值的劍,然后用 delete 洗掉該劍的迭代器(不能是數值!),
記龍的生命為 \(h_i\),回復力為 \(r_i\),則有兩個限制:
- 攻擊后龍的生命模 \(r_i\) 余 \(0\);
- 攻擊后,龍的生命小于等于 \(0\),
問題直接轉化為 \(n\) 對方程構成的方程組
\[\begin{cases} w_ix\equiv h_i\pmod{r_i}\\ w_ix\ge h_i \end{cases} \]不等式方程可以求出 \(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'\) 存在逆元,于是可以得到
\[x\equiv (w_i')^{-1}h_i'\pmod{r_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;
}
THE END
Thanks for reading!
祈愿在風中漸漸冷冽
那一刻她眼底有華光泯滅
海潮呼嘯著哽咽
想把不舍宣泄
所有分別都太決絕
回憶都太熾烈
最后一面何處去借
——《流光幻夜》By 司夏
> Link 流光幻夜-網易云
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/262408.html
標籤:其他
上一篇:「SOL」禮物(BZOJ)
下一篇:「SOL」禮物(BZOJ)
