题解 P6552 【文具订购(加强版)】
Crabby_Maskiv · · 题解
首先给出一个暴力的做法
枚举
由于三组exgcd都可以拿到循环外面做,因此复杂度
我们再添加一个看起来是优化常数的东西:
从大到小枚举
然后再特判根本没有整数解的情况,根据贝祖定理,就是
你会发现,这份代码神奇地通过了本题。
为什么呢?为了证明复杂度,我们需要回想一下 P3951小凯的疑惑 的结论
对于两个互质的正整数 a,b ,ax+by\ (x,y\in N) 最大的不能表示的数是 ab-a-b
也就是说,假设三个数两两互质,对应到这道题上,当
也就是说我们的复杂度变成了
这有啥用呢?
当
否则,不妨假设
那么如果除去三个数两两互质的情况,如何证明复杂度?
我们需要对上面的结论做一个比较显然的变换
对于两个正整数 a,b ,ax+by\ (x,y\in N) 最大的不能表示的 gcd(a,b) 的倍数是 \frac{ab}{gcd(a,b)}-a-b
我们发现,找到解所需的
我们把
这是个线性方程组,解完之后大概形如
由于这个
特别注意,做法中特判无解正好对应了关于
综上这题复杂度
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=200005;
ll d;
pair<ll,ll> exgcd(ll a,ll b){
if(!b){
d=a;
return {1,0};
}
pair<ll,ll> ret,ans=exgcd(b,a%b);
ret.first=ans.second;
ret.second=ans.first-(a/b)*ans.second;
return ret;
}
ll ansa,ansb,ansc;
void upd(ll a,ll b,ll c){
if(a+b+c>ansa+ansb+ansc){
ansa=a;ansb=b;ansc=c;
}
}
ll n,x,y,z;
pair<ll,ll> w1,w2,w3;
ll d1,d2,d3;
int main(){
int i,j;
int T;cin>>T;
while(T--){
cin>>n>>x>>y>>z;
exgcd(x,y);exgcd(z,d);
if(n%d){
cout<<-1<<endl;
continue;
}
ll t;ansa=ansb=ansc=0;
w1=exgcd(x,y);d1=d;
w2=exgcd(y,z);d2=d;
w3=exgcd(x,z);d3=d;
for(t=n/(x+y+z);t>=0;t--){
ll r=n-t*(x+y+z);
ll _x=w1.first,_y=w1.second;d=d1;
if(r%d==0){
_x*=(r/d);
_x=(_x%(y/d)+(y/d))%(y/d);
_y=(r-_x*x)/y;
if(_y>=0) upd(t+_x,t+_y,t);
}
_x=w2.first;_y=w2.second;d=d2;
if(r%d==0){
_x*=(r/d);
_x=(_x%(z/d)+(z/d))%(z/d);
_y=(r-_x*y)/z;
if(_y>=0) upd(t,t+_x,t+_y);
}
_x=w3.first;_y=w3.second;d=d3;
if(r%d==0){
_x*=(r/d);
_x=(_x%(z/d)+(z/d))%(z/d);
_y=(r-_x*x)/z;
if(_y>=0) upd(t+_x,t,t+_y);
}
if(ansa*x+ansb*y+ansc*z==n){
cout<<ansa<<" "<<ansb<<" "<<ansc<<endl;
break;
}
}
if(t<0)
cout<<-1<<endl;
}
return 0;
}
upd:这篇文章中介绍了一种基于类欧几里得的