【校内模拟】游戏达人(BSGS求指标)(类欧几里得)

传送门


TM什么毒瘤省选模拟出类欧几里得。

题解:

由于本身对类欧几里得不是很熟,考场上也没有往类欧几里得方向去想。

结果你TM告诉我最后一步就是类欧几里得

首先特判无解和全零情况。

C x ≡ D y ( m o d p ) C^x\equiv D^y\pmod p CxDy(modp)看上去并不是很好操作,注意到 p p p是质数,也就是说 p p p有原根,那么我们直接求出 C , D C,D C,D的指标 c , d c,d c,d,则现在问题就是 c x ≡ d y ( m o d p − 1 ) cx\equiv dy\pmod {p-1} cxdy(modp1)

首先,如果要解是很好解的,但是现在问题不是解这个方程,而是最小化 A x + B y Ax+By Ax+By。。。考场上没有什么时间去推,也就直接宣告凉凉。

下来滚去orz了题解的推导。

接下来是一系列非常牛逼的等价转换,设现在关于x,y的等价关系为 c x ≡ d y ( m o d p ) cx\equiv dy \pmod p cxdy(modp)

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

g = g c d ( p , c ) g=gcd(p,c) g=gcd(p,c),显然 g c d ( d , g ) = 1 gcd(d,g)=1 gcd(d,g)=1,则 g g g一定是 y y y的因数,我们可以将 B B B乘上 g g g来进行等价变形。

得到的方程形式没有变,设上述变量表示现在的方程,现在设 q = g c d ( p , d ) q=gcd(p,d) q=gcd(p,d),同理 q q q一定是 x x x的因数,则将 A A A乘上 q q q来等价。

现在得到方程 c x ≡ d y ( m o d p ) cx\equiv dy\pmod p cxdy(modp),最小化 A x + B y Ax+By Ax+By

由于我们能够保证现在 g c d ( c , p ) = 1 gcd(c,p)=1 gcd(c,p)=1,即 c c c % p \%p %p下有逆元。方程可以直接转化为 x ≡ d y ( m o d p ) x\equiv dy\pmod p xdy(modp)

考虑到 x , y x,y x,y都是正整数,我们需要最小化 A x + B y Ax+By Ax+By,且 A , B A,B A,B均为正数,显然我们确定了 y y y的时候 x = d y % p = d y − p ⌊ d y p ⌋ x=dy\%p=dy-p\lfloor\frac{dy}{p}\rfloor x=dy%p=dyppdy x x x的最优选择,并且由于 gcd ⁡ ( d , y ) = 1 \gcd(d,y)=1 gcd(d,y)=1,在 y ! = p y!=p y!=p的时候 x x x一定非0,对于 x = y = p x=y=p x=y=p的情况可以直接扔掉,显然不够优秀。

把求值的式子转化一下得到 ( A d + B ) y − A p ⌊ d y p ⌋ , y ∈ [ 1 , p − 1 ] (Ad+B)y-Ap\lfloor\frac{dy}{p}\rfloor,y\in[1,p-1] (Ad+B)yAppdy,y[1,p1]

现在要求的就是 min ⁡ { a y + b ⌊ d y p ⌋ ∣ y ∈ [ l , r ] } \min\{ay+b\lfloor\frac{dy}{p}\rfloor\mid y\in[l,r]\} min{ay+bpdyy[l,r]}

神仙类欧几里得。

f ( l , r , a , b , c , d , e ) = min ⁡ { a y + b ⌊ c y + d e ⌋ ∣ y ∈ [ l , r ] } f(l,r,a,b,c,d,e)=\min\{ay+b\lfloor\frac{cy+d}{e}\rfloor\mid y\in[l,r]\} f(l,r,a,b,c,d,e)=min{ay+becy+dy[l,r]},其中 c , d ≥ 0 , e > 0 c,d\geq 0,e>0 c,d0,e>0

然后是xjb推导,设 p ( y ) = ⌊ c y + d e ⌋ , L = p ( l ) , R = p ( r ) p(y)=\lfloor\frac{cy+d}{e}\rfloor,L=p(l),R=p(r) p(y)=ecy+d,L=p(l),R=p(r)

然后大力分类讨论

  1. L = R L=R L=R根据 A A A的正负性,答案在两个端点取到,直接算一下就行了。
  2. c ≥ e c\geq e ce,类欧几里得常规操作, f ( l , r , a , b , c , d , e ) = f ( l , r , a + b ⌊ c e ⌋ , 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) f(l,r,a,b,c,d,e)=f(l,r,a+bec,b,c%e,d,e)
  3. c &lt; e , a ≥ 0 c&lt; e,a\geq0 c<e,a0,类欧几里得常规操作,此时显然有 L &lt; R L&lt;R L<R,由于 c &lt; e c&lt;e c<e,对于每个 k ∈ [ L , R ] k\in[L,R] k[L,R],存在集合 Y k Y_k Yk,使得 ∀ y ∈ Y k , p ( y ) = k \forall y\in Y_k,p(y)=k yYk,p(y)=k,现在要最小化 a y + b k ay+bk ay+bk,由于 a ≥ 0 a\geq 0 a0,所以现在显然考虑选择最小的 y y y,即 k ≤ p ( y ) &lt; k k\leq p(y)&lt; k kp(y)<k的最小 y y y。解不等式得到 k e − d c ≤ y &lt; k e + e − d c \frac{ke-d}{c}\leq y &lt; \frac{ke+e-d}{c} ckedy<cke+ed
    转化成形式上一样的下取整,即 ⌊ k e − d − 1 c ⌋ &lt; y ≤ ⌊ k e + e − d − 1 c ⌋ \lfloor\frac{ke-d-1}{c}\rfloor&lt;y\leq \lfloor\frac{ke+e-d-1}{c}\rfloor cked1<ycke+ed1
    此时由于 c &lt; e c&lt;e c<e,总能够保证 y y y在该范围内有正整数取值,且最小的 y y y ⌊ k e − d + c − 1 c ⌋ \lfloor\frac{ke-d+c-1}{c}\rfloor cked+c1。发现只有 k = L k=L k=L的情况 y y y有可能取不到该最小值,直接计算即可。
    发现这个形式和上面的一模一样,问题转化为 f ( L + 1 , R , b , a , e , − d + c − 1 , c ) f(L+1,R,b,a,e,-d+c-1,c) f(L+1,R,b,a,e,d+c1,c)
  4. c &lt; e , a &lt; 0 c&lt;e,a &lt; 0 c<e,a<0,还是类欧几里得常规操作, y y y尽量取最大值,不等式和上面一样,特判右端点,问题转化为 f ( L , R − 1 , b , a , e , e − d − 1 , c ) f(L,R-1,b,a,e,e-d-1,c) f(L,R1,b,a,e,ed1,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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值