類歐幾里得演算法學習筆記

VictoryCzt發表於2019-01-09

這個東西看起來好恐怖啊QAQ.jpg搞了幾天,QWQ


其實類歐幾里得演算法,就是和歐幾里得演算法類似(歐幾里得演算法就是gcd那一堆啦),但是其實只有時間複雜度的證明是和gcd一樣的,其它的都完全不同,emmmm。


先前置兩道例題:【luoug-T36097 整點與質數】【[COCI2009-2010#1] ALADIN
】【講解by hdxrie

其實這個就是一個十分簡單的類歐演算法的應用
以前做到的時候還不知道,現在學了才知道,QAQ


我們可以從一個簡單的式子來入門,並對其套路有一定的瞭解。

T105T\leq 10^5組詢問,每組對於給定的n109n\leq 10^9求下面式子在mod 998244353\rm mod\ 998244353意義下的值(a,b0,c>0)(a,b\geq 0,c>0)

i=0nai+bc \sum_{i=0}^n\left\lfloor\frac{ai+b}{c}\right\rfloor

其實前面的ipq\left\lfloor\frac{ip}{q}\right\rfloor就是這個式子的簡化版,b=0b=0

顯然這個不能直接O(n)O(n)算,或者除法分塊,所以我們要另尋它路。

首先我們可以分成兩步考慮:

  1. aca\geq c或者bcb\geq c

此時,我們顯然可以將原式寫成:

i=0n(aci+bc+(a%c)i+(b%c)c) \sum_{i=0}^n\left(\left\lfloor\frac{a}{c}\right\rfloor i+\left\lfloor\frac{b}{c}\right\rfloor+\left\lfloor\frac{(a\%c)i+(b\%c)}{c}\right\rfloor\right)

將其拆開可以得到:

aci=0ni+i=0nbc+i=0n(a%c)i+(b%c)c \left\lfloor\frac{a}{c}\right\rfloor\sum_{i=0}^ni+\sum_{i=0}^n\left\lfloor\frac{b}{c}\right\rfloor+\sum_{i=0}^n\left\lfloor\frac{(a\%c)i+(b\%c)}{c}\right\rfloor

前面兩個可以用公式O(1)O(1)的求,而後面那個又是一個類似的子問題,遞迴求解即可。

所以我們令f(a,b,c,n)=i=0nai+bcf(a,b,c,n)=\sum_{i=0}^n\left\lfloor\frac{ai+b}{c}\right\rfloor,那麼原式可以寫成:

f(a,b,c,n)=acn×(n+1)2+(n+1)bc+f(a%c,b%c,c,n) f(a,b,c,n)=\left\lfloor\frac{a}{c}\right\rfloor\frac{n\times (n+1)}{2}+(n+1)\left\lfloor\frac{b}{c}\right\rfloor+f(a\%c,b\%c,c,n)

此時我們就把問題從ac,bca\geq c,b\geq c轉換a,b<ca,b<c的情況了。

  1. 那麼顯然另外一種情況就是a<ca<cb<cb<c的時候了。

其實,如果看了hdxrie\rm hdxrie的關於[COCI2010]ALADIN\rm [COCI2010]ALADIN的那個題的講解,這個也就是相當於求一條直線下整點個數,具體講解可以看這篇論文【洪華敦-類歐幾里得演算法】。

那麼我們可以列舉整點的座標,原式就可以轉化成(這裡m=an+bc1m=\lfloor\frac{an+b}{c}\rfloor-1):

f(a,b,c,n)=i=0nj=1m+1[jai+bc] f(a,b,c,n)=\sum_{i=0}^n\sum_{j=1}^{m+1}[j\leqslant \left\lfloor\frac{ai+b}{c}\right\rfloor]

這裡[a=1][a=1]這種是邏輯判斷符,如果裡面語句為真,那麼值為1,否則為0。

其實這裡就是相當於一條y=ax+bcy=\frac{ax+b}{c}的直線,所以我們列舉ii就是列舉的xxjj就是yy,如果jj小於等於的話(i,j)(i,j)就是在這個直線下的整點。

然後我們先交換列舉順序可以得到:

由於aba\leqslant \lfloor b\rfloor時也滿足aba\leqslant b,所以變換時可以把向下取整符號去掉。

f(a,b,c,n)=j=1m+1i=0n[jai+bc]=j=0mi=0n[j+1ai+bc]=j=0mi=0n[j+1ai+bc]=j=0mi=0n[jc+cai+b]=j=0mi=0n[aijc+cb]=j=0mi=0n[ai>jc+cb1]=j=0mi=0n[i>jc+cb1a] \begin{aligned} f(a,b,c,n)=&\sum_{j=1}^{m+1}\sum_{i=0}^n[j\leqslant \left\lfloor\frac{ai+b}{c}\right\rfloor] \\ =&\sum_{j=0}^{m}\sum_{i=0}^n[j+1\leqslant \left\lfloor\frac{ai+b}{c}\right\rfloor] \\ =&\sum_{j=0}^{m}\sum_{i=0}^n[j+1\leqslant \frac{ai+b}{c}] \\ =&\sum_{j=0}^{m}\sum_{i=0}^n[jc+c\leqslant ai+b] \\ =&\sum_{j=0}^{m}\sum_{i=0}^n[ai\geqslant jc+c-b] \\ =&\sum_{j=0}^{m}\sum_{i=0}^n[ai>jc+c-b-1] \\ =&\sum_{j=0}^{m}\sum_{i=0}^n[i>\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor] \end{aligned}

由於在i[0,n]i\in[0,n]中的i>jc+cb1ai>\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor的只有jc+cb1an\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor\sim n,所以進而化簡得:

f(a,b,c,n)=j=0m(njc+cb1a)=j=0mnj=0mjc+cb1a= n×(m+1)f(c,cb1,a,m) \begin{aligned} f(a,b,c,n)=&\sum_{j=0}^{m}\left(n-\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor\right) \\ =&\sum_{j=0}^{m}n-\sum_{j=0}^{m}\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor \\ =&\ n\times (m+1)-f(c,c-b-1,a,m) \end{aligned}

那麼這樣又可以將f(a,b,c,n)f(c,cb1,a,an+bc1)f(a,b,c,n)\Rightarrow f(c,c-b-1,a,\left\lfloor\frac{an+b}{c}\right\rfloor-1),又將問題變小了。

那麼邊界就是當:

  • n=0n=0時,直接返回bc\lfloor\frac{b}{c}\rfloor
  • a=0a=0時,就是(n+1)×bc(n+1)\times \lfloor\frac{b}{c}\rfloor

f總結

f(a,b,c,n)={bcn=0(n+1)×bca=0acn×(n+1)2+(n+1)bc+f(a%c,b%c,c,n)ac or bcn×an+bcf(c,cb1,a,m)a<c and b<c f(a,b,c,n)=\begin{cases}\lfloor\frac{b}{c}\rfloor&n=0\\ (n+1)\times \lfloor\frac{b}{c}\rfloor&a=0\\ \left\lfloor\frac{a}{c}\right\rfloor\frac{n\times (n+1)}{2}+(n+1)\left\lfloor\frac{b}{c}\right\rfloor+f(a\%c,b\%c,c,n)&a\geqslant c\ or\ b\geqslant c\\ n\times \left\lfloor\frac{an+b}{c}\right\rfloor-f(c,c-b-1,a,m)&a<c\ and\ b<c\end{cases}

這時就已經可以寫出一份十分簡單的遞迴的程式碼了:

int S1(int n){return n*(n+1)/2;}
int f(int a,int b,int c,int n){
	if(!n) return b/c;
	if(!a) return (n+1)*(b/c);
	if(a>=c||b>=c)return S1(n)*(a/c)+n*(b/c)+f(a%c,b%c,c,n);
	return n*((a*n+b)/c)-f(c,c-b-1,a,(a*n+b)/c-1);
}

關於複雜度,首先我們看ac,bca\geq c,b\geq c的時候只需O(1)O(1)就轉移到了a<c,b<ca<c,b<c的情況了,而這時f(a,b,c,n)f(c,cb1,a,an+bc1)f(a,b,c,n)\Rightarrow f(c,c-b-1,a,\left\lfloor\frac{an+b}{c}\right\rfloor-1)這個就十分類似於歐幾里得演算法的更相減損術,直到a=0a=0n=0n=0時就停止了,那麼複雜度就類似於歐幾里得演算法,是O(logn)O(logn)的。


有了初步的瞭解,我們就可以看看稍微難一點的:

同樣的條件,再求下面兩個式子的值:

i=0n(ai+bc)2 \sum_{i=0}^n\left(\left\lfloor\frac{ai+b}{c}\right\rfloor\right)^2

i=0niai+bc \sum_{i=0}^ni\left\lfloor\frac{ai+b}{c}\right\rfloor


之所以放在一起說,是因為這兩個在求取的過程中會有聯絡,而且還要用到前面的ff

首先我們令h(a,b,c,n)=i=0n(ai+bc)2,g(a,b,c,n)=i=0niai+bch(a,b,c,n)=\sum_{i=0}^n\left(\left\lfloor\frac{ai+b}{c}\right\rfloor\right)^2,g(a,b,c,n)=\sum_{i=0}^ni\left\lfloor\frac{ai+b}{c}\right\rfloor


那麼我們先來看hh的求法,和ff類似(其實類歐的套路都差不多是這樣),我們分兩塊考慮:

  1. aca\geq c或者bcb\geq c時:

顯然同理有:

h(a,b,c,n)=i=0n(aci+bc+(a%c)i+(b%c)c)2 h(a,b,c,n)=\sum_{i=0}^n\left(\left\lfloor\frac{a}{c}\right\rfloor i+\left\lfloor\frac{b}{c}\right\rfloor+\left\lfloor\frac{(a\%c)i+(b\%c)}{c}\right\rfloor\right)^2

我們把那個恐怖的括號開啟,可以得到((a+b+c)2=a2+b2+c2+2ab+2bc+2ac(a+b+c)^2=a^2+b^2+c^2+2ab+2bc+2ac):

h(a,b,c,n)=i=0n(aci)2+i=0n(bc)2+i=0n((a%c)i+(b%c)c)2+i=0n2acibc+i=0n2bc(a%c)i+(b%c)c+i=0n2aci(a%c)i+(b%c)c h(a,b,c,n)=\sum_{i=0}^n\left(\left\lfloor\frac{a}{c}\right\rfloor i\right)^2+\sum_{i=0}^{n}\left(\left\lfloor\frac{b}{c}\right\rfloor\right)^2+\sum_{i=0}^n\left(\left\lfloor\frac{(a\%c)i+(b\%c)}{c}\right\rfloor\right)^2 \\ +\sum_{i=0}^n2\cdot \left\lfloor\frac{a}{c}\right\rfloor i\cdot \left\lfloor\frac{b}{c}\right\rfloor+\sum_{i=0}^n2\cdot\left\lfloor\frac{b}{c}\right\rfloor\cdot\left\lfloor\frac{(a\%c)i+(b\%c)}{c}\right\rfloor+\sum_{i=0}^n2\cdot\left\lfloor\frac{a}{c}\right\rfloor i\cdot\left\lfloor\frac{(a\%c)i+(b\%c)}{c}\right\rfloor

我們把這一大坨,整理化簡一下,可以得到:

由於i=0ni2=n×(n+1)×(2n+1)6\sum_{i=0}^ni^2=\frac{n\times (n+1)\times(2n+1)}{6}i=0ni=n×(n+1)2\sum_{i=0}^ni=\frac{n\times(n+1)}{2}

h(a,b,c,n)=(ac)2n×(n+1)×(2n+1)6+(n+1)×(bc)2+h(a%c,b%c,c,n)+2acbcn×(n+1)2+2bcf(a%c,b%c,c,n)+2acg(a%c,b%c,c,n) h(a,b,c,n)=\left(\left\lfloor\frac{a}{c}\right\rfloor\right)^2\frac{n\times(n+1)\times(2n+1)}{6}+(n+1)\times\left(\left\lfloor\frac{b}{c}\right\rfloor\right)^2+h(a\%c,b\%c,c,n) \\ +2\cdot \left\lfloor\frac{a}{c}\right\rfloor\cdot \left\lfloor\frac{b}{c}\right\rfloor\cdot\frac{n\times(n+1)}{2}+2\cdot\left\lfloor\frac{b}{c}\right\rfloor\cdot f(a\%c,b\%c,c,n)+2\cdot\left\lfloor\frac{a}{c}\right\rfloor\cdot g(a\%c,b\%c,c,n)

遞迴處理就可以了(假設我們現在知道gg如何計算)。

  1. 我們繼續考慮當a<c,b<ca<c,b<c的時候的情況:

通過ff的經驗,我們照著之前的變換一下看(mm的意義同前面):

h(a,b,c,n)=i=0n(j=1m+1[jai+bc])2 h(a,b,c,n)=\sum_{i=0}^n\left(\sum_{j=1}^{m+1}[j\leqslant \left\lfloor\frac{ai+b}{c}\right\rfloor]\right)^2

我們將後面拆成兩個式子,然後按照ff的方式變換:

h(a,b,c,n)=i=0n(j=1m+1[jai+bc])×(k=1m+1[kai+bc])=i=0n(j=0m[j+1ai+bc])×(k=0m[k+1ai+bc])=i=0n(j=0m[jc+cai+b])×(k=0m[kc+cai+b])=i=0n(j=0m[aijc+cb])×(k=0m[aikc+cb])=i=0n(j=0m[ai>jc+cb1])×(k=0m[ai>kc+cb1]) \begin{aligned} h(a,b,c,n)=&\sum_{i=0}^n\left(\sum_{j=1}^{m+1}[j\leqslant \left\lfloor\frac{ai+b}{c}\right\rfloor]\right)\times \left(\sum_{k=1}^{m+1}[k\leqslant \left\lfloor\frac{ai+b}{c}\right\rfloor]\right) \\ =&\sum_{i=0}^n\left(\sum_{j=0}^{m}[j+1\leqslant \frac{ai+b}{c}]\right)\times \left(\sum_{k=0}^{m}[k+1\leqslant \frac{ai+b}{c}]\right) \\ =&\sum_{i=0}^n\left(\sum_{j=0}^{m}[jc+c\leqslant {ai+b}]\right)\times \left(\sum_{k=0}^{m}[kc+c\leqslant {ai+b}]\right) \\ =&\sum_{i=0}^n\left(\sum_{j=0}^{m}[ai\geqslant jc+c-b]\right)\times \left(\sum_{k=0}^{m}[ai\geqslant {kc+c-b}]\right) \\ =&\sum_{i=0}^n\left(\sum_{j=0}^{m}[ai> jc+c-b-1]\right)\times \left(\sum_{k=0}^{m}[ai> {kc+c-b-1}]\right) \end{aligned}

然後我們同理,交換列舉順序,可以得到:

h(a,b,c,n)=j=0mk=0mi=0n[ai>max(jcb1,kcb1)]=j=0mk=0mi=0n[i>max(jc+cb1a,kc+cb1a)] \begin{aligned} h(a,b,c,n)=&\sum_{j=0}^m\sum_{k=0}^m\sum_{i=0}^n[ai>\max(jc-b-1,kc-b-1)] \\ =&\sum_{j=0}^m\sum_{k=0}^m\sum_{i=0}^n[i>\max(\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor,\left\lfloor\frac{kc+c-b-1}{a}\right\rfloor)] \end{aligned}

同樣,i[0,n]i\in[0,n]i>max(jc+cb1a,kc+cb1a)i>\max(\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor,\left\lfloor\frac{kc+c-b-1}{a}\right\rfloor)的只有>max(jc+cb1a,kc+cb1a)n>\max(\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor,\left\lfloor\frac{kc+c-b-1}{a}\right\rfloor)\sim n這一部分,所以原式還可以寫成:

h(a,b,c,n)=j=0mk=0m(nmax(jc+cb1a,kc+cb1a))=n(m+1)2j=0mk=0mmax(jc+cb1a,kc+cb1a) \begin{aligned} h(a,b,c,n)=&\sum_{j=0}^{m}\sum_{k=0}^{m}\left(n-\max(\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor,\left\lfloor\frac{kc+c-b-1}{a}\right\rfloor)\right) \\ =& n(m+1)^2-\sum_{j=0}^{m}\sum_{k=0}^{m}\max(\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor,\left\lfloor\frac{kc+c-b-1}{a}\right\rfloor) \end{aligned}

這裡的max\max不好處理,但是我們可以通過列舉當前這個是max\max的貢獻,首先jkj\geqslant k的部分為:

j=0m(j+1)jc+cb1a \sum_{j=0}^{m}(j+1)\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor

因為對於每個jj,都有0j0\sim j也就是j+1j+1kk比它小,所以每個的貢獻次數是j+1j+1次。

那麼同理對於jkj\leqslant k的部分就是:
k=0m(k+1)kc+cb1a \sum_{k=0}^{m}(k+1)\left\lfloor\frac{kc+c-b-1}{a}\right\rfloor

但是這兩個加起來,會把j=kj=k的部分多加一次,所以我們減去多加了的這個部分:

j=0mjc+cb1a \sum_{j=0}^{m}\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor

那麼原式就可以變成:

h(a,b,c,n)=n(m+1)2j=0mk=0mmax(jc+cb1a,kc+cb1a)=n(m+1)2(j=0m(j+1)jc+cb1a+k=0m(k+1)kc+cb1aj=0mjc+cb1a)=n(m+1)2(2j=0m(j+1)jc+cb1aj=0mjc+cb1a) \begin{aligned} h(a,b,c,n)=&n(m+1)^2-\sum_{j=0}^{m}\sum_{k=0}^{m}\max(\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor,\left\lfloor\frac{kc+c-b-1}{a}\right\rfloor) \\ =&n(m+1)^2-\left(\sum_{j=0}^{m}(j+1)\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor+\sum_{k=0}^{m}(k+1)\left\lfloor\frac{kc+c-b-1}{a}\right\rfloor-\sum_{j=0}^{m}\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor\right) \\ =&n(m+1)^2-\left(2\cdot \sum_{j=0}^{m}(j+1)\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor-\sum_{j=0}^{m}\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor\right) \end{aligned}

我們把(j+1)(j+1)開啟,然後整理可以得到:

h(a,b,c,n)=n(m+1)2(2j=0mjjc+cb1a+2j=0mjc+cb1aj=0mjc+cb1a)=n(m+1)2(2j=0mjjc+cb1a+j=0mjc+cb1a)=n(m+1)22g(c,cb1,a,m)f(c,cb1,a,m) \begin{aligned} h(a,b,c,n)=&n(m+1)^2-\left(2\cdot \sum_{j=0}^{m}j\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor+2\cdot\sum_{j=0}^{m}\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor-\sum_{j=0}^{m}\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor\right) \\ =&n(m+1)^2-\left(2\cdot \sum_{j=0}^{m}j\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor+\sum_{j=0}^{m}\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor\right) \\ =&n(m+1)^2-2\cdot g(c,c-b-1,a,m)-f(c,c-b-1,a,m) \end{aligned}

那麼我們同樣可以將其轉移到更小的問題上,遞迴計算即可。

邊界同理:

  • n=0n=0時,就是(bc)2\left(\frac{b}{c}\right)^2
  • a=0a=0時,就是(n+1)×(bc)2(n+1)\times \left(\frac{b}{c}\right)^2

h總結

h(a,b,c,n)={(bc)2n=0(n+1)×(bc)2a=0(ac)2n(n+1)(2n+1)6+(n+1)(bc)2+h(a%c,b%c,c,n)+2acbcn(n+1)2+2bcf(a%c,b%c,c,n)+2acg(a%c,b%c,c,n)ac or bcn(m+1)22g(c,cb1,a,m)f(c,cb1,a,m)a<c and b<c h(a,b,c,n)=\begin{cases}\left(\frac{b}{c}\right)^2&n=0\\ (n+1)\times \left(\frac{b}{c}\right)^2&a=0\\ \left(\left\lfloor\frac{a}{c}\right\rfloor\right)^2\frac{n(n+1)(2n+1)}{6}+(n+1)\left(\left\lfloor\frac{b}{c}\right\rfloor\right)^2+h(a\%c,b\%c,c,n) +2\left\lfloor\frac{a}{c}\right\rfloor\left\lfloor\frac{b}{c}\right\rfloor\frac{n(n+1)}{2}+2\left\lfloor\frac{b}{c}\right\rfloor f(a\%c,b\%c,c,n)+2\left\lfloor\frac{a}{c}\right\rfloor g(a\%c,b\%c,c,n)&a\geqslant c\ or\ b\geqslant c\\ n(m+1)^2-2\cdot g(c,c-b-1,a,m)-f(c,c-b-1,a,m)&a<c\ and\ b<c\end{cases}

程式碼同樣十分好寫,仿照上面的即可,這裡就不寫了。


接下來只剩下對於gg的求法了。

gg相對hh來說就很好變換了,直接跟著套路走:

  1. ac,bca\geqslant c,b\geqslant c時:

g(a,b,c,n)=i=0niai+bc=i=0naci2+bci+i(a%c)i+(b%c)c=acn(n+1)(2n+1)6+bcn(n+1)2+g(a%c,b%c,c,n) \begin{aligned} g(a,b,c,n)=&\sum_{i=0}^ni\left\lfloor\frac{ai+b}{c}\right\rfloor \\ =&\sum_{i=0}^n\left\lfloor\frac{a}{c}\right\rfloor i^2+\left\lfloor\frac{b}{c}\right\rfloor i+i\left\lfloor\frac{(a\%c)i+(b\%c)}{c}\right\rfloor \\ =&\left\lfloor\frac{a}{c}\right\rfloor\frac{n(n+1)(2n+1)}{6}+\left\lfloor\frac{b}{c}\right\rfloor\frac{n(n+1)}{2}+g(a\%c,b\%c,c,n) \end{aligned}

  1. a<c,b<ca<c,b<c

根據套路,我們轉換成這個形式:

g(a,b,c,n)=j=0mi=0ni[i>jc+cb1a]=j=0mi=jc+cb1a+1ni=j=0m(i=0nii=0jc+cb1ai)=j=0mn(n+1)2jc+cb1a(jc+cb1a+1)2=12(n(n+1)(m+1)j=0m(jc+cb1a)2jc+cb1a)=12(n(n+1)(m+1)h(c,cb1,a,m)f(c,cb1,a,m)) \begin{aligned} g(a,b,c,n)=&\sum_{j=0}^{m}\sum_{i=0}^ni[i>\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor] \\ =&\sum_{j=0}^{m}\sum_{i=\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor+1}^ni \\ =&\sum_{j=0}^{m}\left(\sum_{i=0}^ni-\sum_{i=0}^{\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor}i\right) \\ =&\sum_{j=0}^{m}\frac{n(n+1)}{2}-\frac{\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor\cdot\left(\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor+1\right)}{2} \\ =&\frac{1}{2}\left(n(n+1)(m+1)-\sum_{j=0}^{m}\left(\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor\right)^2-\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor\right) \\ =&\frac{1}{2}\left(n(n+1)(m+1)-h(c,c-b-1,a,m)-f(c,c-b-1,a,m)\right) \end{aligned}

由此我們可以直接判斷後遞迴計算即可。

邊界條件還是一樣的:

  • n=0n=0時,為00
  • a=0a=0時,為bc(n(n+1)2)\lfloor\frac{b}{c}\rfloor(\frac{n(n+1)}{2})

g總結

g(a,b,c,n)={0n=0bc(n(n+1)2)a=0acn(n+1)(2n+1)6+bcn(n+1)2+g(a%c,b%c,c,n)ac or bc12(n(n+1)(m+1)h(c,cb1,a,m)f(c,cb1,a,m))a<c and b<c g(a,b,c,n)=\begin{cases}0&n=0\\ \lfloor\frac{b}{c}\rfloor(\frac{n(n+1)}{2})&a=0\\ \left\lfloor\frac{a}{c}\right\rfloor\frac{n(n+1)(2n+1)}{6}+\left\lfloor\frac{b}{c}\right\rfloor\frac{n(n+1)}{2}+g(a\%c,b\%c,c,n)&a\geqslant c\ or\ b\geqslant c\\ \frac{1}{2}\left(n(n+1)(m+1)-h(c,c-b-1,a,m)-f(c,c-b-1,a,m)\right)& a<c\ and\ b<c\end{cases}

程式碼同理。


技巧:為了方便求取g,hg,h,通常我們可以在一個遞迴裡面寫,可以降低複雜度,並且寫起了也挺簡單的。

這個算是個簡單的模板了,求的就是上面講的f,h,gf,h,g函式:

//程式碼中的h,g的意義對換了一下,懶得改了
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll Mod=998244353;
const ll inv2=499122177,inv6=166374059;
ll S1(ll x){if(x>=Mod)x%=Mod;return (x*(x+1)%Mod)*inv2%Mod;}
ll S2(ll x){if(x>=Mod)x%=Mod;return (x*(x+1)%Mod*(x+x+1)%Mod)*inv6%Mod;}
ll Sqr(ll x){return x*x%Mod;}
struct node{
	ll f,g,h;
	void clear(){f=g=h=0;}
	node(){}
	node(ll a,ll b,ll c):f(a),g(b),h(c){}
	void out(){printf("%lld %lld %lld\n",f,g,h);}
};
node calc(ll a,ll b,ll c,ll n){
	node ans,res;ans.clear();
	ll m,t1,t2,s1,s2;
	if(!n){ans.f=b/c;ans.g=Sqr(b/c);return ans;}
	if(!a){
		t1=b/c;
		ans.f=(n+1ll)*t1%Mod;
		ans.g=(n+1ll)*Sqr(t1)%Mod;
		ans.h=S1(n)*t1%Mod;
		return ans;
	}
	if(a>=c||b>=c){
		t1=a/c;t2=b/c;
		res=calc(a%c,b%c,c,n);
		s1=S1(n);s2=S2(n);
		ans.f=(((s1*t1%Mod)+(n+1ll)*t2%Mod)%Mod+res.f)%Mod;
		ans.g=(((Sqr(t1)*s2%Mod+(n+1ll)*Sqr(t2)%Mod)%Mod)+((t1*t2%Mod)*2ll*s1%Mod+(t1*2ll*res.h%Mod))%Mod+(res.g+t2*2ll*res.f%Mod)%Mod)%Mod;
		ans.h=((s2*t1%Mod+s1*t2%Mod)+res.h)%Mod;
		return ans;
	}
	m=(n*a+b)/c-1;
	res=calc(c,c-b-1,a,m);
	ll w1=n*(m+1)%Mod,w2=n*(n+1)%Mod,w3=m+1;
	if(w3>=Mod)w3%=Mod;
	ans.f=(w1-res.f)%Mod;if(ans.f<0)ans.f+=Mod;
	ans.g=((w1*w3)%Mod-((res.h*2ll%Mod+res.f)%Mod))%Mod;if(ans.g<0)ans.g+=Mod;
	ans.h=((w2*w3)%Mod-(res.f+res.g)%Mod)%Mod*inv2%Mod;if(ans.h<0)ans.h+=Mod;
	return ans;
}
int a,b,c,n,T;
int main(){
	for(scanf("%d",&T);T--;){scanf("%d%d%d%d",&n,&a,&b,&c);calc(a,b,c,n).out();}
	return 0;
}

我們其實可以求取更加一般化的式子,如這道模板題【IN-loj

題意就是多組詢問,給定k1,k2,a,b,c,nk_1,k_2,a,b,c,n求下面式子在mod 998244353\rm mod\ 998244353意義下的值。

i=0nik1ai+bck2 \sum_{i=0}^ni^{k_1}\left\lfloor\frac{ai+b}{c}\right\rfloor^{k_2}

資料範圍見原題面。

  • k1=0,k2=1k_1=0,k_2=1時,就是ff函式。
  • k1=0,k2=2k_1=0,k_2=2時,就是hh函式。
  • k1=1,k2=1k_1=1,k_2=1時,就是gg函式。

那麼當k1,k2k_1,k_2不為上面的那些情況的時候應該怎麼辦呢?

我們還是按照套路來分析,我們令F(k1,k2,a,b,c,n)=i=0nik1ai+bck2F(k_1,k_2,a,b,c,n)=\sum_{i=0}^ni^{k_1}\left\lfloor\frac{ai+b}{c}\right\rfloor^{k_2}

  • ac,bca\geq c,b\geq c時:

F(k1,k2,a,b,c,n)=i=0nik1(aci+bc+(a%c)i+(b%c)c)k2 F(k_1,k_2,a,b,c,n)=\sum_{i=0}^ni^{k_1}\left(\left\lfloor\frac{a}{c}\right\rfloor i+\left\lfloor\frac{b}{c}\right\rfloor+\left\lfloor\frac{(a\%c)i+(b\%c)}{c}\right\rfloor\right)^{k_2}

用二項式定理,我們可以將後面的括號展開得到:

用二項式定理推導一下可以得到
(a+b+c)n=i+j+k=nCniCnijaibjck(a+b+c)^n=\sum_{i+j+k=n}C_{n}^iC_{n-i}^ja^ib^jc^k

F(k1,k2,a,b,c,n)=I=0ni+j+k=k2Ck2iCk2ijacibcj(Ik1+i)(a%c)I+(b%c)ck=i+j+k=k2Ck2iCk2ijacibcjI=0n(Ik1+i)(a%c)I+(b%c)ck0i,j,k F(k_1,k_2,a,b,c,n)=\sum_{I=0}^n\sum_{i+j+k=k_2}C_{k_2}^iC_{k_2-i}^j\left\lfloor\frac{a}{c}\right\rfloor^i\left\lfloor\frac{b}{c}\right\rfloor^j\left(I^{k_1+i}\right)\left\lfloor\frac{(a\%c)I+(b\%c)}{c}\right\rfloor^{k} \\ =\sum_{i+j+k=k_2}C_{k_2}^iC_{k_2-i}^j\left\lfloor\frac{a}{c}\right\rfloor^i\left\lfloor\frac{b}{c}\right\rfloor^j\sum_{I=0}^n\left(I^{k_1+i}\right)\left\lfloor\frac{(a\%c)I+(b\%c)}{c}\right\rfloor^{k} \\ 0\leq i,j,k

我們可以發現,最後面那堆等於F(k1+i,k,a%c,b%c,c,n)F(k_1+i,k,a\%c,b\%c,c,n),所以遞迴處理即可。

  • a&lt;c,b&lt;ca&lt;c,b&lt;c的時候:

我們知道xk=i=0x1(i+1)kikx^k=\sum_{i=0}^{x-1}(i+1)^k-i^k

因為其等於:xk(x1)k+(x1)k(x2)k+0k=xk0=xkx^k-(x-1)^k+(x-1)^k-(x-2)^k+\cdots-0^k=x^k-0=x^k

那麼我們用其來變換原式得到:

F(k1,k2,a,b,c,n)=i=0nik1j=0ai+bc1(j+1)k2jk2 F(k_1,k_2,a,b,c,n)=\sum_{i=0}^ni^{k_1}\sum_{j=0}^{\left\lfloor\frac{ai+b}{c}\right\rfloor-1}(j+1)^{k_2}-j^{k_2}

我們交換列舉順序可以得到(m=an+bc1m=\left\lfloor\frac{an+b}{c}\right\rfloor-1):

F(k1,k2,a,b,c,n)=j=0m((j+1)k2jk2)i=0nik1[jai+bc]=j=0m((j+1)k2jk2)i=0nik1[i&lt;jc+cb1a]=j=0m((j+1)k2jk2)(i=0nik1i=0jc+cb1aik1) \begin{aligned} F(k_1,k_2,a,b,c,n)=&amp;\sum_{j=0}^{m}\left((j+1)^{k_2}-j^{k_2}\right)\sum_{i=0}^ni^{k_1}[j\leq\left\lfloor\frac{ai+b}{c}\right\rfloor] \\ =&amp;\sum_{j=0}^{m}\left((j+1)^{k_2}-j^{k_2}\right)\sum_{i=0}^ni^{k_1}[i&lt;\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor] \\ =&amp;\sum_{j=0}^{m}\left((j+1)^{k_2}-j^{k_2}\right)\left(\sum_{i=0}^{n}i^{k_1}-\sum_{i=0}^{\lfloor\frac{jc+c-b-1}{a}\rfloor}i^{k_1}\right) \end{aligned}

我們接下來將前面那一堆(也就是相當於(m+1)k2(m+1)^{k_2})乘進後面,得到:

F(k1,k2,a,b,c,n)=j=0m((j+1)k2jk2)i=0nik1j=0m((j+1)k2jk2)i=0jc+cb1aik1=(m+1)k2i=0nik1j=0m((j+1)k2jk2)i=0jc+cb1aik1 \begin{aligned} F(k_1,k_2,a,b,c,n)=&amp;\sum_{j=0}^{m}\left((j+1)^{k_2}-j^{k_2}\right)\sum_{i=0}^n i^{k_1}-\sum_{j=0}^{m}\left((j+1)^{k_2}-j^{k_2}\right)\sum_{i=0}^{\lfloor\frac{jc+c-b-1}{a}\rfloor}i^{k_1} \\ =&amp;(m+1)^{k_2}\sum_{i=0}^n i^{k_1}-\sum_{j=0}^{m}\left((j+1)^{k_2}-j^{k_2}\right)\sum_{i=0}^{\lfloor\frac{jc+c-b-1}{a}\rfloor}i^{k_1} \end{aligned}

而我們知道,關於自然數冪的字首和是一個關於冪次+1的多項式,所以我們可以用拉格朗日插值(或者高斯消元)把其係數求出來,然後就可以帶入到後面那一堆,再用二項式定理展開,得到:

假設i=0k+1ak,ixi=i=0xik\sum_{i=0}^{k+1}a_{k,i}x^i=\sum_{i=0}^xi^k

F(k1,k2,a,b,c,n)=(m+1)k2i=0nik1j=0m((j+1)k2jk2)i=0jc+cb1aik1=(m+1)k2i=0nik1k=0k21(j=0mCk2kjki=0jc+cb1aik1)=(m+1)k2i=0nik1k=0k21(j=0mCk2kjki=0k1+1ak1,i(jc+cb1a)i)=(m+1)k2i=0nik1k=0k21i=0k1+1Ck2kak1,ij=0mjk(jc+cb1a)i=(m+1)k2i=0nik1k=0k21i=0k1+1Ck2kak1,iF(k,i,c,cb1,a,m) \begin{aligned} F(k_1,k_2,a,b,c,n)=&amp;(m+1)^{k_2}\sum_{i=0}^n i^{k_1}-\sum_{j=0}^{m}\left((j+1)^{k_2}-j^{k_2}\right)\sum_{i=0}^{\lfloor\frac{jc+c-b-1}{a}\rfloor}i^{k_1} \\ =&amp;(m+1)^{k_2}\sum_{i=0}^n i^{k_1}-\sum_{k=0}^{k_2-1}\left(\sum_{j=0}^mC_{k_2}^kj^k\cdot \sum_{i=0}^{\lfloor\frac{jc+c-b-1}{a}\rfloor}i^{k_1}\right) \\ =&amp;(m+1)^{k_2}\sum_{i=0}^n i^{k_1}-\sum_{k=0}^{k_2-1}\left(\sum_{j=0}^mC_{k_2}^kj^k\cdot \sum_{i=0}^{k_1+1}a_{k_1,i}\left(\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor\right)^i\right) \\ =&amp;(m+1)^{k_2}\sum_{i=0}^n i^{k_1}-\sum_{k=0}^{k_2-1}\sum_{i=0}^{k_1+1}C_{k_2}^ka_{k_1,i}\cdot \sum_{j=0}^mj^k\left(\left\lfloor\frac{jc+c-b-1}{a}\right\rfloor\right)^i \\ =&amp;(m+1)^{k_2}\sum_{i=0}^n i^{k_1}-\sum_{k=0}^{k_2-1}\sum_{i=0}^{k_1+1}C_{k_2}^ka_{k_1,i}\cdot F(k,i,c,c-b-1,a,m) \end{aligned}

其中,有一步是這樣變換的:
i=0x1(i+1)kik=i=0x1j=0kCkjijik=i=0x1j=0k1Ckjij=j=0k1(i=0x1Ckjij) \sum_{i=0}^{x-1}(i+1)^k-i^k=\sum_{i=0}^{x-1}\sum_{j=0}^kC_{k}^ji^j-i^k \\=\sum_{i=0}^{x-1}\sum_{j=0}^{k-1}C_{k}^{j}i^j \\=\sum_{j=0}^{k-1}\left(\sum_{i=0}^{x-1}C_{k}^ji^j\right)

那麼,這個和前面的其實一樣,遞迴計算就好啦,只不過要提前預處理一下係數和組合數。

k1,k2k_1,k_2比較小,所以每次算的時候暴力列舉一下(或者可以用FFTFFT之類的優化一下)就行了。

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int M=13;
const ll Mod=1e9+7;
ll y[M],C[M][M],F[M][M][44];
ll fpow(ll a,ll b=Mod-2){
	a%=Mod;if(a<0)a+=Mod;ll res=1;for(;b;b>>=1,a=(a*a)%Mod)if(b&1)res=(res*a)%Mod;
	return res;
}
struct Ply{
	ll x[M];
	void clear(){memset(x,0,sizeof(x));}
	Ply(){memset(x,0,sizeof(x));}
	Ply operator +(const Ply &a)const{
		Ply ans;for(int i=0;i<M;i++)ans.x[i]=(x[i]+a.x[i])%Mod;
		return ans;	
	}
	Ply operator *(const Ply &a)const{
		Ply ans;
		for(int i=0;i<M;i++)for(int j=0;i+j<M;j++)
		ans.x[i+j]=(ans.x[i+j]+x[i]*a.x[j]%Mod)%Mod;
		return ans;
	}
	ll operator ()(ll a,int b){
		ll ans=x[b+1];for(int i=b;i>=0;i--)ans=(ans*a%Mod+x[i])%Mod;
		return ans;
	}
}A[M],B[M];
void calc(Ply &a,int n){
	Ply xx,yy;
	for(int i=0;i<=n;i++){
		xx.x[0]=y[i];
		for(int j=0;j<=n;j++)if(i^j)xx.x[0]=xx.x[0]*fpow(i-j)%Mod;
		for(int j=0;j<=n;j++)if(j^i){yy.x[0]=Mod-j;yy.x[1]=1;xx=xx*yy;}
		a=a+xx;xx.clear();
	}
}
ll f(int k1,int k2,ll a,ll b,ll c,ll n,int d=0){
	if(~F[k1][k2][d]) return F[k1][k2][d];
	ll ans=0;
	if(!a) ans=B[k1](n,k1)*fpow(b/c,k2)%Mod;
	else if(!k2) ans=B[k1](n,k1);
	else if(a>=c||b>=c){
		int x=a/c,y=b/c;a%=c;b%=c;
		for(ll i=0,X=1;i<=k2;i++,X=(X*x)%Mod){
			for(ll j=0,Y=X;i+j<=k2;j++,Y=Y*y%Mod){
				ans=(ans+f(k1+i,k2-i-j,a,b,c,n,d+1)*C[k2][i]%Mod*C[k2-i][j]%Mod*Y%Mod)%Mod;
			}
		}
	}else{
		int m=(a*n+b)/c;--m;
		ans=A[k2](m,k2);
		ans=ans*B[k1](n,k1)%Mod;
		for(int j=0;j<k2;j++)for(int i=0;i<=k1+1;i++){
			ans=(ans-f(j,i,c,c-b-1,a,m,d+1)*C[k2][j]%Mod*B[k1].x[i]%Mod)%Mod;
		}
	}
	if(ans<0)(ans%=Mod)+=Mod;ans%=Mod;
	return F[k1][k2][d]=ans;
}
void init(){
	C[0][0]=1;
	for(int i=1;i<=10;i++){
		C[i][0]=C[i][i]=1;
		for(int j=1;j<i;j++){
			C[i][j]=(C[i-1][j]+C[i-1][j-1])%Mod;
		}
	}
	for(int i=0;i<=10;i++){
		if(i)y[0]=1;
		for(int j=1;j<=i+1;j++){
			y[j]=(y[j-1]+fpow(j+1,i)-fpow(j,i))%Mod;
			if(y[j]<0)y[j]+=Mod;
		}
		calc(A[i],i+1);
		if(!i)y[0]=1;else y[0]=0;
		for(int j=1;j<=i+1;j++)y[j]=(y[j-1]+fpow(j,i))%Mod;
		calc(B[i],i+1);
	}
}
int T,a,b,c,n,k1,k2;
int main(){
	init();
	for(scanf("%d",&T);T--;){
		scanf("%d%d%d%d%d%d",&n,&a,&b,&c,&k1,&k2);
		memset(F,-1,sizeof(F));
		printf("%d\n",f(k1,k2,a,b,c,n));
	}
	return 0;
} 

題目:

LOJ模板題的另外兩個講解:


End

相關文章