一直以为这个是计数内容
第一次看见是在 djq 的论文里,还以为是新东西( ,不过我确实还没学就是了。
称之为欧几里得的东西,想必一定利用 \(\bmod\) 和 \(swap\) 达到了 \(\log\) 复杂度的目的。
这种数形结合的形式还真是有趣呢 \(\smile\) 。
所谓万能欧几里得,是解决这样一种问题的:
在平面直角坐标系上的一次函数 \(y = \frac{Px + R}{Q}\),从 \(x=0\) 扫到 \(x=L\) ,每次若直线碰到横线就执行 \(U\) ,如果碰到竖线就执行 \(R\) ,其中 \(U,R\) 是指两个具有结合律的操作,钦定经过整点时先执行 \(U\) 。
Step 1. \(P \bmod Q\)
若 \(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\) 之前,列出式子然后调整一下取整函数。
这样的推导同时也保证了操作的优先级正确。
几个问题:
- \(-R-1<0\)
- 原本 \(x=L\) 的地方可能 \(y\) 不是整数。
针对第一个问题,由于我们知道 \(P<Q,R<Q\) ,所以 \(Q-R-1 \ge 0\) ,也即 \(x=1\) 处取值一定为正,那么把这前面一段先计算了放在前面就行。
嗯这里提到了 \(R<Q\) ,原因是如果 \(R \ge Q\) 那我们完全可以将其直接提出常数个 \(U\) 。
针对第二个问题,也是把后面的一段另外计算即可,知道了总共有多少个 \(U\) 和 \(R\) ,算这个还是很简单的。
实际应用时要注意 \(U,R\) 表示成多元组形式的结合。
应用于类欧模板(省去 include 和 define):
(高度拟合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;
}