傳送門
TM什麼毒瘤省選模擬出類歐幾里得。
題解:
由於本身對類歐幾里得不是很熟,考場上也沒有往類歐幾里得方向去想。
結果你TM告訴我最後一步就是類歐幾里得
首先特判無解和全零情況。
看上去並不是很好操作,注意到是質數,也就是說有原根,那麼我們直接求出的指標,則現在問題就是
首先,如果要解是很好解的,但是現在問題不是解這個方程,而是最小化。。。考場上沒有什麼時間去推,也就直接宣告涼涼。
下來滾去orz了題解的推導。
接下來是一系列非常牛逼的等價轉換,設現在關於x,y的等價關係爲
首先同除以三者gcd,現在
設,顯然,則一定是的因數,我們可以將乘上來進行等價變形。
得到的方程形式沒有變,設上述變量表示現在的方程,現在設,同理一定是的因數,則將乘上來等價。
現在得到方程,最小化。
由於我們能夠保證現在,即在下有逆元。方程可以直接轉化爲。
考慮到都是正整數,我們需要最小化,且均爲正數,顯然我們確定了的時候是的最優選擇,並且由於,在的時候一定非0,對於的情況可以直接扔掉,顯然不夠優秀。
把求值的式子轉化一下得到
現在要求的就是
神仙類歐幾里得。
設,其中。
然後是xjb推導,設
然後大力分類討論
- 根據的正負性,答案在兩個端點取到,直接算一下就行了。
- ,類歐幾里得常規操作,
- ,類歐幾里得常規操作,此時顯然有,由於,對於每個,存在集合,使得,現在要最小化,由於,所以現在顯然考慮選擇最小的,即的最小。解不等式得到
轉化成形式上一樣的下取整,即
此時由於,總能夠保證在該範圍內有正整數取值,且最小的爲。發現只有的情況有可能取不到該最小值,直接計算即可。
發現這個形式和上面的一模一樣,問題轉化爲 - ,還是類歐幾里得常規操作,儘量取最大值,不等式和上面一樣,特判右端點,問題轉化爲
然後就是找一下原根,然後BSGS求指標。
注意詢問有點多,BSGS需要魔改塊長。
然後我平生第一次知道了sqrt算負數會自動取unsigned值
代碼:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const
using std::cout;
using std::cerr;
int p,gr;
inline int power(int a,int b,int res=1){
for(;b;b>>=1,a=(ll)a*a%p)(b&1)&&(res=(ll)res*a%p);
return res;
}
struct Map{
static cs int magic=4898597;
int key[magic],val[magic],waste;
Map(){memset(key,-1,sizeof key);}
int locate(int k)cs{
int h=k%magic;
while(key[h]!=-1&&key[h]!=k)h=(h+1)%magic;
return h;
}
int &operator[](int k){
int h=locate(k);
if(key[h]==-1)key[h]=k;
return val[h];
}
cs int &operator[](int k)cs{
int h=locate(k);
return key[h]==-1?waste:val[h];
}
bool find(int k)cs{return key[locate(k)]==k;}
}ma;
inline void get_gr(){
int phi=p-1;
std::vector<int> pr;
for(int re i=2;(ll)i*i<=phi;++i){
if(phi%i==0){
pr.push_back(i);
while(phi%i==0)phi/=i;
}
}
if(phi>1)pr.push_back(phi);
for(gr=2;;++gr){
bool flag=true;
for(int p:pr)if(power(gr,(::p-1)/p)==1){flag=false;break;}
if(flag)break;
}
}
int step,ibase;
inline void init(int T){
get_gr();//cout<<"gr : "<<gr<<"\n";
step=ceil(sqrt((ll)T*p));
int now=1;
for(int re i=0;i<step;++i){
ma[now]=i;
now=(ll)now*gr%p;
}
ibase=power(now,p-2);
}
inline int get_ind(int v){
for(int re i=0;;++i,v=(ll)v*ibase%p){
if(ma.find(v))
return i*step+ma[v];
}
assert(0);
return -1;
}
inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
inline void ex_gcd(ll a,ll b,ll &x,ll &y){
if(!b){x=1,y=0;return ;}ex_gcd(b,a%b,y,x);y-=a/b*x;
}
inline ll inv(ll a,ll b){
ll x,y;ex_gcd(a,b,x,y);
return (x%b+b)%b;
}
inline ll f(ll l,ll r,ll a,ll b,ll c,ll d,ll e){
if(l>r)return 1ll<<60;
ll L=(c*l+d)/e,R=(c*r+d)/e;
if(L==R)return std::min(a*l,a*r)+L*b;
if(c>=e)return f(l,r,a+b*(c/e),b,c%e,d,e);
if(a>=0)return std::min(a*l+b*L,f(L+1,R,b,a,e,c-d-1,c));
return std::min(a*r+b*R,f(L,R-1,b,a,e,e-d-1,c));
}
inline void solve(){
ll a,b,c,d;
scanf("%lld%lld%lld%lld",&a,&b,&c,&d);
c%=p,d%=p;
if((c==0)^(d==0)){cout<<"Damn it\n";return ;}
if(c==0){cout<<a+b<<"\n";return ;}
c=get_ind(c),d=get_ind(d);int p=::p-1;
int g=gcd(gcd(c,d),p);
c/=g,d/=g,p/=g;
g=gcd(c,p);c/=g,p/=g,b*=g;
d=(ll)d*inv(c,p)%p;
g=gcd(d,p);d/=g,p/=g,a*=g;
cout<<std::min((a+b)*p,f(1,p-1,a*d+b,-a*p,d,0,p))<<"\n";
}
signed main(){
#ifdef zxyoi
freopen("game.in","r",stdin);
#endif
int T;scanf("%d%d",&p,&T);init(T);
while(T--)solve();
return 0;
}