(伪)再扩展中国剩余定理(洛谷P4774 [NOI2018]屠龙勇士)(中国剩余定理,扩展欧几里德,multiset)
前言
我们熟知的中国剩余定理,在使用条件上其实是很苛刻的,要求模线性方程组的模数两两互质。
于是就有了扩展中国剩余定理,其实现方法大概是通过扩展欧几里德把两个同余方程合并,具体会在下面提到。
但是,使用仍有限制,那就是的系数必须为。
没关系,把它再扩展一下
题目及实现
题意分析
显然,如果我们能干掉所有龙,那么每一次使用的剑的攻击力是已知的,设为。那么对于每一条龙,攻击次数必须满足;当然别忘记要先把生命值降到非正数,也就是说还要满足即。
可以看出不能直接用扩展中国剩余定理,但不妨碍先介绍一下它。
扩展中国剩余定理
如果有一个模线性方程组,每个形如,而且不保证两两互质,就用不了中国剩余定理了。
扩展中国剩余定理是这样做的。
我们先把问题简化到一个方程组只有两个方程的情况,形如
把它写成不定方程的形式
这样就可以合并了,解一下,所以可变号
发现变成了一个二元一次不定方程的样子,设,用扩展欧几里德求中的一个解,于是用可以表示的解集(关于一般二元一次不定方程的解法和解的周期性证明可以看看蒟蒻之前写的一篇题解)
把解集带回得到
这样,我们设初始方程为,每次合并两个方程得到新的方程。当然中途如果有一次出现不为整数则整个方程组无解。
再扩展
那么模线性方程组,每个形如该怎么解好呢?
还是要合并方程。仍然设一个总方程,将它与当前方程合并。
下面是同步赛上手推的式子,请直接跳过这一段,因为式子是错的,只有45分。说不定有些Dalao和我的想法一样?
直接合并
设,解不定方程得到一个解,有
为什么不能直接合并呢?因为连当前能不能解都没有考虑。
所以正确的方法应该是先解当前方程。
设,解,得即
方程里的系数被去掉了!可以从求逆元的角度来理解这一个过程。
当然,会有一些特殊情况。如果且,方程恒成立,我们不把它与总方程合并。如果且,显然无解。
最后就是用扩展中国剩余定理合并啦。
具体实现
确定每次攻击使用的剑,直接用multiset解决,二分找到满足要求的剑(比较舒服的是upper_bound找第一个比要求大的剑,如果等于begin-iterator的话就说明没有不大于要求值的,直接选它,否则--iterator就是满足要求的),把它删掉,再加入当前奖励的剑。注意删的时候删的是iterator而不是数字。
再次提醒要注意。可以求出,如果最后总方程的小于它,则要补至满足条件的最小值,用式子写一下大概是(可以理解成,现在c和max还有差距,但是为了保证c模m的值不变,所以一次次给c加上m直到大于等于max)。
注意数据范围,会爆longlong的地方用快速乘。注意处理负数。
多组数据,注意清空和初始化。
#include<cstdio>
#include<set>
#define RG register
#define R RG LL
#define GC c=getchar()
using namespace std;
typedef long long LL;
const int N=1e5+9;
LL a[N],p[N],b[N],X,Y,G;
multiset<LL>s;
inline LL in(){
RG char GC;
while(c<'-')GC;
R x=c&15;GC;
while(c>'-')x*=10,x+=c&15,GC;
return x;
}
void exgcd(R a,R b){
if(!b){X=1;Y=0;G=a;return;}
exgcd(b,a%b);
(Y^=X^=Y^=X)-=a/b*X;
}
inline LL mul(R b,R k,R m){//快速乘
R a=0;
for(;k;k>>=1,b=(b<<1)%m)
if(k&1)a=(a+b)%m;
return a;
}
int main(){
freopen("dragon.in","r",stdin);
freopen("dragon.out","w",stdout);
R T=in(),n,m,i,c,k,mx;
RG multiset<LL>::iterator it;
E:while(T--){
n=in();m=in();
for(i=1;i<=n;++i)a[i]=in();
for(i=1;i<=n;++i)p[i]=in();
for(i=1;i<=n;++i)b[i]=in();
s.clear();//注意清空
for(i=1;i<=m;++i)s.insert(in());
mx=c=0;m=1;//初始总方程
for(i=1;i<=n;++i){
it=s.upper_bound(a[i]);//谨慎选择lower_bound和upper_bound
if(it!=s.begin())--it;
k=*it;s.erase(it);s.insert(b[i]);//小心手一滑erase(*it)(居然还有90分)
mx=max(mx,(a[i]-1)/k+1);//更新限制
k%=p[i];a[i]%=p[i];//开始解方程,去掉系数
if(!k&&a[i]){puts("-1");goto E;}
if(!k&&!a[i])continue;//这两个要特判
exgcd(k,p[i]);
if(a[i]%G){puts("-1");goto E;}
p[i]/=G;
a[i]=mul(a[i]/G,(X%p[i]+p[i])%p[i],p[i]);
exgcd(m,p[i]);//开始合并,X和a-c都可能是负数
if((a[i]-c)%G){puts("-1");goto E;}
m=m/G*p[i];
c=(c+mul(mul(m/p[i],((a[i]-c)%m+m)%m,m),(X%m+m)%m,m))%m;
}
printf("%lld\n",c>=mx?c:c+m*((mx-c-1)/m+1));//满足限制
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具