题目链接
https://www.luogu.com.cn/problem/P4774
题解
首先,根据题意,我们可以很容易预处理出面对第 \(i\) 条龙时使用的剑,记其伤害值为 \(c_i\)。
做完了这样一步预处理过后,会发现我们实际上要解若干个形如 \(c_ix\equiv a_i \pmod{p_i}\) 这样的同余方程组联立后的最小解。
解这样的方程组,第一个前提条件是 \(a_i \leq p_i\),如果不满足这个条件的话,可能会出现龙的血量在 \(\bmod\ p_i\) 意义下为零,但实际血量不为零的问题。
不过好在不满足 \(a_i \leq p_i\) 的测试数据都满足 \(p_i=1\),这意味着这种情况下的答案将直接为 \(\max \left \lceil \frac{a_i}{c_i} \right \rceil\)。
排除这种情况后,我们下面的讨论均在 \(a_i \leq p_i\) 的前提下进行。
这个方程组看起来像是可以应用扩展中国剩余定理(ExCRT)的样子。但是 ExCRT 要求 \(x\) 前面的系数必须为 \(1\)。
将方程组两边同时除 \(c_i\)?不行,模 \(p_i\) 意义下 \(c_i\) 的逆元不一定存在。
考虑将线性同余方程改写成不定方程的形式。
像 \(c_ix\equiv a_i \pmod{p_i}\) 就可以改写为:\(c_ix+p_iy=a_i\)。
接下来用扩展欧几里得算法来解这个方程组即可。
等等!扩展欧几里得算法解的是形如 \(ax+by=\gcd(a,b)\) 形式的不定方程,上面的方程显然不满足这个形式,不能直接用 exgcd 来解。
换个元后稍微推一下:
我们设 \(x=\frac{a_i}{\gcd(c_i,p_i)}x’\),\(y=\frac{a_i}{\gcd(c_i,p_i)}y’\)。
则原来的方程可以改写为:
$$
c_ix’+p_iy’=\gcd(c_i,p_i)
$$
现在方程变成了标准形式,可以直接解了。
设上面方程的一组解是 \(x’=x’_0\),\(y’=y’_0\),则原方程与之对应的解为 \(x_0=\frac{a_i}{\gcd(c_i,p_i)}x’_0\),\(y_0=\frac{a_i}{\gcd(c_i,p_i)}y’_0\)。
这是一个方程组的解,对于多个方程组,我们需要用 ExCRT(本质上还是扩展欧几里得)来合并它们。
简单来说,对于两个线性同余方程:
$$
\begin{cases}
x \equiv a_1 \pmod {p_1}\\
x \equiv a_2 \pmod {p_2}\\
\end{cases}
$$
我们还是把它们改写成不定方程的形式:
$$
\begin{cases}
x = a_1 +p_1y_1\\
x = a_2 +p_2y_2\\
\end{cases}
$$
联立后消去 \(x\),用扩展欧几里得解 \(y_1,y_2\) 即可合并两个方程。
#include <iostream> #include <set> #include <algorithm> using namespace std; long long a[100005],p[100005]; long long nswd[100005],c[100005]; multiset<long long> swd; int n,m; long long exgcd(long long a,long long b,long long&x,long long&y) { if(b==0) { x=1,y=0; return a; } long long g=exgcd(b,a%b,x,y); int t=x; x=y; y=t-a/b*y; return g; } long long mul(long long x,long long y,long long p) { long long ans=0; while(y) { if(y&1)ans=(ans+x)%p; x=(x+x)%p; y>>=1; } return ans; } namespace sub1//p_i maybe greater than a_i, but guaranteed p_i=1 { bool check() { bool flag=true; for(int i=1;i<=n;i++) flag&=(p[i]==1); return flag; } void solve() { long long ans=0; for(int i=1;i<=n;i++) ans=max(ans,(long long)((a[i]/c[i])+(a[i]%c[i]!=0))); cout<<ans<<endl; } } int main() { ios::sync_with_stdio(false); int t; cin>>t; while(t--) { bool flag=true; cin>>n>>m; for(int i=1;i<=n;i++) cin>>a[i]; for(int i=1;i<=n;i++) cin>>p[i]; for(int i=1;i<=n;i++) cin>>nswd[i]; swd.clear(); for(int i=1;i<=m;i++) { long long x; cin>>x; swd.insert(x); } for(int i=1;i<=n;i++) { auto it=swd.upper_bound(a[i]); if(it!=swd.begin())it--; c[i]=*it; swd.erase(it); swd.insert(nswd[i]); } long long ans=0,z=1; if(sub1::check()) { sub1::solve(); continue; } for(int i=1;i<=n;i++) { c[i]%=p[i],a[i]%=p[i]; if(!c[i]&&a[i]) { cout<<-1<<endl; flag=false;break; } if(!c[i]&&!a[i])continue; long long x,y; long long g=exgcd(c[i],p[i],x,y); if(a[i]%g) { cout<<-1<<endl; flag=false;break; } p[i]/=g; a[i]=mul(a[i]/g,(x%p[i]+p[i])%p[i],p[i]); g=exgcd(z,p[i],x,y); if((a[i]-ans)%g) { cout<<-1<<endl; flag=false;break; } z=z/g*p[i]; ans=(ans+mul(mul(z/p[i],((a[i]-ans)%z+z)%z,z),(x%z+z)%z,z))%z; } if(flag)cout<<ans<<endl; } return 0; }