【校內模擬】遊戲達人(BSGS求指標)(類歐幾里得)

傳送門


TM什麼毒瘤省選模擬出類歐幾里得。

題解:

由於本身對類歐幾里得不是很熟,考場上也沒有往類歐幾里得方向去想。

結果你TM告訴我最後一步就是類歐幾里得

首先特判無解和全零情況。

CxDy(modp)C^x\equiv D^y\pmod p看上去並不是很好操作,注意到pp是質數,也就是說pp有原根,那麼我們直接求出C,DC,D的指標c,dc,d,則現在問題就是cxdy(modp1)cx\equiv dy\pmod {p-1}

首先,如果要解是很好解的,但是現在問題不是解這個方程,而是最小化Ax+ByAx+By。。。考場上沒有什麼時間去推,也就直接宣告涼涼。

下來滾去orz了題解的推導。

接下來是一系列非常牛逼的等價轉換,設現在關於x,y的等價關係爲cxdy(modp)cx\equiv dy \pmod p

首先同除以三者gcd,現在gcd(c,d,p)=1gcd(c,d,p)=1

g=gcd(p,c)g=gcd(p,c),顯然gcd(d,g)=1gcd(d,g)=1,則gg一定是yy的因數,我們可以將BB乘上gg來進行等價變形。

得到的方程形式沒有變,設上述變量表示現在的方程,現在設q=gcd(p,d)q=gcd(p,d),同理qq一定是xx的因數,則將AA乘上qq來等價。

現在得到方程cxdy(modp)cx\equiv dy\pmod p,最小化Ax+ByAx+By

由於我們能夠保證現在gcd(c,p)=1gcd(c,p)=1,即cc%p\%p下有逆元。方程可以直接轉化爲xdy(modp)x\equiv dy\pmod p

考慮到x,yx,y都是正整數,我們需要最小化Ax+ByAx+By,且A,BA,B均爲正數,顯然我們確定了yy的時候x=dy%p=dypdypx=dy\%p=dy-p\lfloor\frac{dy}{p}\rfloorxx的最優選擇,並且由於gcd(d,y)=1\gcd(d,y)=1,在y!=py!=p的時候xx一定非0,對於x=y=px=y=p的情況可以直接扔掉,顯然不夠優秀。

把求值的式子轉化一下得到(Ad+B)yApdyp,y[1,p1](Ad+B)y-Ap\lfloor\frac{dy}{p}\rfloor,y\in[1,p-1]

現在要求的就是min{ay+bdypy[l,r]}\min\{ay+b\lfloor\frac{dy}{p}\rfloor\mid y\in[l,r]\}

神仙類歐幾里得。

f(l,r,a,b,c,d,e)=min{ay+bcy+dey[l,r]}f(l,r,a,b,c,d,e)=\min\{ay+b\lfloor\frac{cy+d}{e}\rfloor\mid y\in[l,r]\},其中c,d0,e>0c,d\geq 0,e>0

然後是xjb推導,設p(y)=cy+de,L=p(l),R=p(r)p(y)=\lfloor\frac{cy+d}{e}\rfloor,L=p(l),R=p(r)

然後大力分類討論

  1. L=RL=R根據AA的正負性,答案在兩個端點取到,直接算一下就行了。
  2. cec\geq e,類歐幾里得常規操作,f(l,r,a,b,c,d,e)=f(l,r,a+bce,b,c%e,d,e)f(l,r,a,b,c,d,e)=f(l,r,a+b\lfloor\frac{c}{e}\rfloor,b,c\%e,d,e)
  3. c<e,a0c< e,a\geq0,類歐幾里得常規操作,此時顯然有L<RL<R,由於c<ec<e,對於每個k[L,R]k\in[L,R],存在集合YkY_k,使得yYk,p(y)=k\forall y\in Y_k,p(y)=k,現在要最小化ay+bkay+bk,由於a0a\geq 0,所以現在顯然考慮選擇最小的yy,即kp(y)<kk\leq p(y)< k的最小yy。解不等式得到kedcy<ke+edc\frac{ke-d}{c}\leq y < \frac{ke+e-d}{c}
    轉化成形式上一樣的下取整,即ked1c<yke+ed1c\lfloor\frac{ke-d-1}{c}\rfloor<y\leq \lfloor\frac{ke+e-d-1}{c}\rfloor
    此時由於c<ec<e,總能夠保證yy在該範圍內有正整數取值,且最小的yyked+c1c\lfloor\frac{ke-d+c-1}{c}\rfloor。發現只有k=Lk=L的情況yy有可能取不到該最小值,直接計算即可。
    發現這個形式和上面的一模一樣,問題轉化爲f(L+1,R,b,a,e,d+c1,c)f(L+1,R,b,a,e,-d+c-1,c)
  4. c<e,a<0c<e,a < 0,還是類歐幾里得常規操作,yy儘量取最大值,不等式和上面一樣,特判右端點,問題轉化爲f(L,R1,b,a,e,ed1,c)f(L,R-1,b,a,e,e-d-1,c)

然後就是找一下原根,然後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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章