「万能欧几里得」学习笔记

一直以为这个是计数内容

第一次看见是在 djq 的论文里,还以为是新东西( ,不过我确实还没学就是了。

称之为欧几里得的东西,想必一定利用 \(\bmod\)\(swap\) 达到了 \(\log\) 复杂度的目的。
这种数形结合的形式还真是有趣呢 \(\smile\)


所谓万能欧几里得,是解决这样一种问题的:

在平面直角坐标系上的一次函数 \(y = \frac{Px + R}{Q}\),从 \(x=0\) 扫到 \(x=L\) ,每次若直线碰到横线就执行 \(U\) ,如果碰到竖线就执行 \(R\) ,其中 \(U,R\) 是指两个具有结合律的操作,钦定经过整点时先执行 \(U\)

Step 1. \(P \bmod Q\)

\[y = \lfloor \frac{Px+R}{Q} \rfloor = \lfloor \frac P Q \rfloor x + \lfloor \frac{(P \bmod Q)x + R}{Q} \rfloor \]

\(P \ge Q\) ,则在每个 \(R\) 前一定至少有 \(\lfloor \frac P Q \rfloor\)\(U\) ,那么把 \(\lfloor \frac P Q \rfloor\)\(U\)\(R\) 拼到一起,有 \(f(P,Q,R,L,U,R) = f(P \bmod Q ,Q,R,L,U,U^{\lfloor \frac{P}{Q} \rfloor}R)\)

Step 2. \(swap(P,Q)\)

不就是对称翻转嘛

将原本的函数以 \(y=x\) 为对称轴翻转,也就是 \(y=\lfloor \frac{Px+R}{Q} \rfloor\) 变成了 \(y=\lfloor \frac{Qx-R-1}{P} \rfloor\) ,具体推导是设第 \(a\)\(R\) 在第 \(b\)\(U\) 之前,列出式子然后调整一下取整函数。
这样的推导同时也保证了操作的优先级正确。

几个问题:

  1. \(-R-1<0\)
  2. 原本 \(x=L\) 的地方可能 \(y\) 不是整数。

针对第一个问题,由于我们知道 \(P<Q,R<Q\) ,所以 \(Q-R-1 \ge 0\) ,也即 \(x=1\) 处取值一定为正,那么把这前面一段先计算了放在前面就行。
嗯这里提到了 \(R<Q\) ,原因是如果 \(R \ge Q\) 那我们完全可以将其直接提出常数个 \(U\)

针对第二个问题,也是把后面的一段另外计算即可,知道了总共有多少个 \(U\)\(R\) ,算这个还是很简单的。


实际应用时要注意 \(U,R\) 表示成多元组形式的结合。

应用于类欧模板(省去 includedefine):
(高度拟合pb)

using namespace std;
typedef long long ll;
const int mod=998244353;
int T,n,a,b,c;
struct node{
	int a,b,s1,s2,s3;
	node(){a=b=s1=s2=s3=0;}
	node(int A,int B,int C,int D,int E){a=A;b=B;s1=C;s2=D;s3=E;}
	friend inline node operator*(node f,node g){
		node ret;
		ret.a=add(f.a,g.a);
		ret.b=add(f.b,g.b);
		ret.s1=((ll)f.a*g.b + f.s1 + g.s1) % mod;
		ret.s2=((ll)f.a*f.a%mod*g.b%mod + 2ll*f.a*g.s1%mod + f.s2 + g.s2) % mod;
		ret.s3=(((ll)f.a * g.b + g.s1) % mod * f.b % mod + (ll)g.b*(g.b+1)/2 % mod * f.a % mod + f.s3 + g.s3 ) % mod;
		return ret;
	}
}U,R,I;
node power(node x,int n){
	node a;
	while(n){
		if(n&1)a=a*x;
		x=x*x;
		n>>=1;
	}
	return a;
}
node f(int P,int Q,int R,ll L,node a,node b){
	if(!L)return node();
	if(!(((ll)L*P+R)/Q))return power(b,L);
	if(P>=Q)return f(P%Q,Q,R,L,a,power(a,P/Q)*b);
	// if(!P)return node(0,L,0,0,0);
	node ret=power(b,(Q-R-1)/P)*a;
	ll len=((ll)L*P+R)/Q;
	ret=ret*f(Q,P,(Q-R-1)%P,len-1,b,a);
	ret=ret*power(b,L-((ll)len*Q-R-1)/P);
	return ret;
}
int main(){
	freopen("data.in","r",stdin);
	freopen("data.out","w",stdout);
	U.a=R.b=1;
	scanf("%d",&T);
	while(T--){
		scanf("%d%d%d%d",&n,&a,&b,&c);
		I.a=b/c;
		node ans=f(a,c,b%c,n,U,R);
		ans=node(b/c,0,0,0,0)*ans;
		printf("%d %d %d\n",add(ans.s1,(b/c))%mod,((ll)(b/c)*(b/c)+ans.s2)%mod,ans.s3);
	}

	return 0;
}
posted @ 2022-06-08 10:14  Kelvin2005  阅读(89)  评论(0)    收藏  举报