我还是觉得我学这玩意纯属闲着没事干。
类欧几里得算法
定义
\[f(a,b,c,n)=\sum_{i=0}^n\left\lfloor\frac{ai+b}c\right\rfloor
\]
让你 \(O(\log n)\) 计算这个东西。
首先假如 \(a\ge c\) 或 \(b\ge c\) 我们显然可以把它们对 \(c\) 取模后加上什么东西。推一下式子。
设 \(a'=a\bmod c,b'=b\bmod c\)。
\[\begin{aligned}
f(a,b,c,n)=&\sum_{i=0}^n\left\lfloor\frac{ai+b}c\right\rfloor\\
=&\sum_{i=0}^n\left\lfloor\frac{(\lfloor\frac ac\rfloor c+a')i+\lfloor\frac bc\rfloor c+b'}c\right\rfloor\\
=&\sum_{i=0}^n\left\lfloor\frac ac\right\rfloor i+\left\lfloor\frac bc\right\rfloor+\left\lfloor\frac{a'i+b'}{c}\right\rfloor\\
=&\left\lfloor\frac ac\right\rfloor\frac{n(n+1)}2+\left\lfloor\frac bc\right\rfloor(n+1)+f(a',b',c,n)
\end{aligned}
\]
然后考虑 \(a<c,b<c\) 的情况怎么搞。以下是这类长的像类欧的东西怎么推的大概套路。
\[\begin{aligned}
f(a,b,c,n)=&\sum_{i=0}^n\left\lfloor\frac{ai+b}c\right\rfloor\\
=&\sum_{i=0}^n\sum_{j=0}^{\left\lfloor\frac{ai+b}c\right\rfloor-1}1\\
=&\sum_{j=0}^{\lfloor\frac{an+b}c\rfloor-1}\sum_{i=0}^n\left[j<\left\lfloor\frac{ai+b}c\right\rfloor\right]
\end{aligned}
\]
把后边的拆一下:
\[\begin{aligned}
&\left[j<\left\lfloor\frac{ai+b}c\right\rfloor\right]\\
=&\left[j+1\le\left\lfloor\frac{ai+b}c\right\rfloor\right]\\
=&\left[j+1\le\frac{ai+b}c\right]\\
=&[jc+c\le ai+b]\\
=&[jc+c-b-1<ai]\\
=&\left[i>\left\lfloor\dfrac{jc+c-b-1}a\right\rfloor\right]
\end{aligned}
\]
代入原式:
\[\begin{aligned}
&\sum_{j=0}^{\lfloor\frac{an+b}c\rfloor-1}\sum_{i=0}^n\left[i>\left\lfloor\dfrac{jc+c-b-1}a\right\rfloor\right]\\
=&\sum_{j=0}^{\lfloor\frac{an+b}c\rfloor-1}\left(n-\left\lfloor\dfrac{jc+c-b-1}a\right\rfloor\right)\\
=&n\left\lfloor\frac{an+b}c\right\rfloor-f(c,c-b-1,a,\left\lfloor\frac{an+b}c\right\rfloor-1)
\end{aligned}
\]
发现这里分母 \(c\) 和分子 \(a\) 互换,可以继续取模向下递归。终止条件当 \(a=0\) 时 \(f(a,b,c,n)=n\left\lfloor\dfrac bc\right\rfloor\)。
int f(int a,int b,int c,int n){
if(!a)return b/c*(n+1);
if(a>=c||b>=c)return n*(n+1)/2*(a/c)+(n+1)*(b/c)+f(a%c,b%c,c,n);
int m=(a*n+b)/c;
return n*m-f(c,c-b-1,a,m-1);
}
然后继续推板子的另外两个式子:
\[g(a,b,c,n)=\sum_{i=0}^ni\left\lfloor\dfrac{ai+b}c\right\rfloor
\]
\[h(a,b,c,n)=\sum_{i=0}^n\left\lfloor\dfrac{ai+b}c\right\rfloor^2
\]
g
先看 \(a,b\) 对 \(c\) 取模后的变化。以下均设 \(a'=a\bmod c,b'=b\bmod c,m=\left\lfloor\dfrac{an+b}c\right\rfloor,t=\left\lfloor\dfrac{jc+c-b-1}a\right\rfloor\)。
\[\begin{aligned}
g(a,b,c,n)=&\sum_{i=0}^ni\left\lfloor\frac{ai+b}c\right\rfloor\\
=&\sum_{i=0}^ni\left\lfloor\frac{(\lfloor\frac ac\rfloor c+a')i+\lfloor\frac bc\rfloor c+b'}c\right\rfloor\\
=&\sum_{i=0}^n\left\lfloor\frac ac\right\rfloor i^2+\left\lfloor\frac bc\right\rfloor i+\left\lfloor\frac{a'i+b'}c\right\rfloor\\
=&\left\lfloor\frac ac\right\rfloor\frac{n(n+1)(2n+1)}6+\left\lfloor\frac bc\right\rfloor\frac{n(n+1)}2+g(a',b',c,n)
\end{aligned}
\]
\[\begin{aligned}
g(a,b,c,n)=&\sum_{i=0}^ni\left\lfloor\frac{ai+b}c\right\rfloor\\
=&\sum_{i=0}^ni\sum_{j=0}^{\lfloor\frac{ai+b}c\rfloor}1\\
=&\sum_{j=0}^{m-1}\sum_{i=0}^ni\left[i<\left\lfloor\frac{ai+b}c\right\rfloor\right]\\
=&\sum_{j=0}^{m-1}\sum_{i=0}^ni[i>t]\\
=&\sum_{j=0}^{m-1}\frac{(n+t+1)(n-t)}2\\
=&\frac 12\left(mn(n+1)-h(c,c-b-1,a,m-1)-f(c,c-b-1,a,m-1)\right)
\end{aligned}
\]
h
\[\begin{aligned}
h(a,b,c,n)=&\sum_{i=0}^n\left\lfloor\frac{ai+b}c\right\rfloor^2\\
=&\sum_{i=0}^n\left\lfloor\frac{(\lfloor\frac ac\rfloor c+a')i+\lfloor\frac bc\rfloor c+b'}c\right\rfloor^2\\
=&\sum_{i=0}^n\left(\left\lfloor\frac ac\right\rfloor i+\left\lfloor\frac bc\right\rfloor +\left\lfloor\frac{a'i+b'}c\right\rfloor\right)^2\\
=&\sum_{i=0}^n\left(\left\lfloor\frac ac\right\rfloor i\right)^2+\left\lfloor\frac bc\right\rfloor^2+\left\lfloor\frac{a'i+b'}c\right\rfloor^2+2\left\lfloor\frac ac\right\rfloor\left\lfloor\frac bc\right\rfloor i+2\left\lfloor\frac ac\right\rfloor\left\lfloor\frac{a'i+b'}c\right\rfloor+2\left\lfloor\frac bc\right\rfloor\left\lfloor\frac{a'i+b'}c\right\rfloor\\
=&h(a',b',c,n)+2\left\lfloor\frac bc\right\rfloor f(a',b',c,n)+2\left\lfloor\frac ac\right\rfloor g(a',b',c,n)+\\
&\left\lfloor\frac ac\right\rfloor^2\frac{n(n+1)(2n+1)}6+\left\lfloor\frac bc\right\rfloor^2(n+1)+\left\lfloor\frac ac\right\rfloor\left\lfloor\frac bc\right\rfloor n(n+1)
\end{aligned}
\]
首先可以把 \(n^2\) 化为
\[2\dfrac{n(n+1)}2-n=2\sum_{i=0}^ni-n
\]
来避免化简的时候出现两个求和号相乘。
\[\begin{aligned}
h(a,b,c,n)=&\sum_{i=0}^n\left\lfloor\frac{ai+b}c\right\rfloor^2\\
=&\sum_{i=0}^n\left(2\sum_{j=0}^{\lfloor\frac{ai+b}c\rfloor}j-\left\lfloor\frac{ai+b}c\right\rfloor\right)\\
=&2\sum_{i=0}^n\sum_{j=1}^{\lfloor\frac{ai+b}c\rfloor}j-f(a,b,c,n)
\end{aligned}
\]
看前面一半:
\[\begin{aligned}
&\sum_{i=0}^n\sum_{j=1}^{\lfloor\frac{ai+b}c\rfloor}j\\
=&\sum_{i=0}^n\sum_{j=0}^{\lfloor\frac{ai+b}c\rfloor-1}j+1\\
=&\sum_{j=0}^{m-1}(j+1)\sum_{i=0}^n\left[j<\left\lfloor\frac{ai+b}c\right\rfloor\right]\\
=&\sum_{j=0}^{m-1}(j+1)(n-t)\\
=&\frac 12nm(m+1)-\sum_{j=0}^{m-1}(j+1)t\\
\end{aligned}
\]
所以有
\[h(a,b,c,n)=nm(m+1)-2g(c,c-b-1,a,m-1)-2f(c,c-b-1,a,m-1)-f(a,b,c,n)
\]
这三个可以一块算。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define int long long
using namespace std;
const int mod=998244353,inv2=499122177,inv6=166374059;
int n,a,b,c;
struct node{
int f,g,h;
node(){f=g=h=0;}
};
node solve(int a,int b,int c,int n){
node ans;
int m=(1ll*a*n+b)/c,ac=a/c%mod,bc=b/c%mod;
if(!a){
ans.f=1ll*bc*(n+1)%mod;
ans.g=1ll*n*(n+1)%mod*inv2%mod*bc%mod;
ans.h=1ll*bc*bc%mod*(n+1)%mod;
return ans;
}
if(a>=c||b>=c){
ans.f=(1ll*n*(n+1)%mod*inv2%mod*ac%mod+1ll*(n+1)*bc%mod)%mod;
ans.g=(1ll*n*(n+1)%mod*(2*n+1)%mod*inv6%mod*ac%mod+1ll*n*(n+1)%mod*inv2%mod*bc%mod)%mod;
ans.h=(1ll*n*(n+1)%mod*(2*n+1)%mod*inv6%mod*ac%mod*ac%mod+
1ll*(n+1)*bc%mod*bc%mod+
1ll*n*(n+1)%mod*ac%mod*bc%mod)%mod;
node d=solve(a%c,b%c,c,n);
ans.f=(ans.f+d.f)%mod;
ans.g=(ans.g+d.g)%mod;
ans.h=(ans.h+d.h+2ll*bc%mod*d.f%mod+2ll*ac%mod*d.g%mod)%mod;
return ans;
}
node d=solve(c,c-b-1,a,m-1);
ans.f=(1ll*n*m%mod-d.f+mod)%mod;
ans.g=(1ll*n*(n+1)%mod*m%mod-d.h-d.f+mod+mod)%mod*inv2%mod;
ans.h=(1ll*n*m%mod*(m+1)%mod-2ll*d.g%mod-2ll*d.f%mod-ans.f+3ll*mod)%mod;
return ans;
}
signed main(){
int tim;scanf("%lld",&tim);
while(tim--){
scanf("%lld%lld%lld%lld",&n,&a,&b,&c);
node ans=solve(a,b,c,n);
printf("%lld %lld %lld\n",ans.f,ans.h,ans.g);
}
return 0;
}
下面是几个题。
P5171 Earthquake
大板板题。题意是求这个:
\[\sum_{x=0}^{\lfloor\frac ca\rfloor}\left\lfloor\frac{-ax+c}b\right\rfloor+1
\]
上边挂个负数没法类欧,那假设 \(b>a\) (如果相反可以交换),那么加一个 \(x\) 减一个 \(x\) 有:
\[\sum_{x=0}^{\lfloor\frac ca\rfloor}\left\lfloor\frac{(b-a)x+c}b\right\rfloor-x+1
\]
算就行了。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define int long long
using namespace std;
int n,a,b,c;
int f(int a,int b,int c,int n){
if(!a)return b/c*(n+1);
int ans=0;
if(a>=c||b>=c){
ans=n*(n+1)/2*(a/c)+(n+1)*(b/c)+f(a%c,b%c,c,n);
return ans;
}
int m=(a*n+b)/c;
int d=f(c,c-b-1,a,m-1);
ans=n*m-d;
return ans;
}
signed main(){
scanf("%lld%lld%lld",&a,&b,&c);
if(a>b)swap(a,b);
printf("%lld\n",f(b-a,c,b,c/a)-(c/a)*(c/a+1)/2+c/a+1);
return 0;
}
[COCI2009-2010#1] ALADIN
首先这题我没写纯口胡的。因为这题线段树还 tm 要离散化而且恶意卡空间(动态开点容易 MLE,空间 64MB 时间 8s 然而基本最大的点都是 1s 过的)我不知道什么意味。
首先关于这个修改操作,线段树每个节点可以记录一个 \(L,A,B\),这样这个区间的和就可以类欧算。线段树查询就正常查就行了。然而代码不是一般的恶心。复杂度 \(O(q\log^2n)\)。
万能欧几里得到时候再说。