類歐幾里得入土總結 2

類歐幾里得入土總結 2

直接說 LOJ萬能歐幾里得這道題

題目要我們求這個式子

\[\sum^n_{x=1} A^xB^{ax+b\over c} \]

問題轉換

考慮一件這樣的事情,平面上一條直線\(ax+b\over c\),關於這條直線,我們放到網格圖上,從左到右,我們遇到一條橫線執行一次\(U\)操作,遇到一次豎線執行一次\(R\)操作,經過一個整點,執行UR操作。U代表迭代一次\(\rm curB*=B\),R代表迭代一次\(\rm curA*=A\)然後累加一遍\({\rm sum+=}(\rm curA)\times ({\rm curB})\),這相當於在模擬這個\(\sum\)的操作,我們稱\(UR\)構成的序列叫做操作序列。

若此處的\(<+,\times>\)有兩者有結合律,\(+\)要對$\times $有分配律,x,y是操作序列,xy表示直接首尾相接起來。我們容易證明以下等式

\[\cases{ \rm curA(xy)=curA(x)\times curA(y) \\ \rm curB(xy)=curB(x)\times curB(y) \\ \rm sum(xy)=sum(x)+curA(x)\times sum(y)\times curB(x) } \]

也就是說那麼操作序列具有結合律

記五元組\((n,a,b,c,x,y)=s\)表示我要生成一個操作序列\(s\),規則是對於直線\(f(x)=y=\lfloor {ax+b\over c}\rfloor,x\in [1,n]\) ,放在網格上,從左下往右上走,每經過一個豎線添加一段操作數列\(y\),每經過一個橫線添加一段操作序列\(x\)(同時經過先進行\(x\)操作)。

注意到這個\(s\)總共有\(n\)\(y\),第\(i\)\(y\)之前有\({ai+b\over c}\)\(x\)

沿用【瞎講】類歐幾里得入土教程的思路,我們考慮將\(a,b\)都化到小於\(c\)的情況。

一些鋪墊


重要補充

\(g(i)\)表示在第\(i\)\(y\)之前有多少個\(x\),我們有

\[g(i)=\begin{cases}f(1)&i=1\\f(i)-f(i-1) &\rm otherwise\end{cases} \]

定義\(f\)的和定義和\(g\)等價,描述的是同一個情況,爲了方便理解建議用差分的角度理解(i到i-1個y之間有多少個x)


先考慮\(b\ge c\)的情況,我們要使得$b\to b%c $

\(i>1\)\(y\)之前有\(f(i)-f(i-1)\)\(x\),而\(b\to b\% c\)對這個差分毫無影響。

然而\(i=1\)的時候有小問題,第一個\(y\)之前個\(x\)我們不是差分定義的,而我們使得\(b\to b\%c\),使得在第一個\(y\)之前少了一些\(y\),具體的,少了\(\lfloor{a\times 1+b\over c}\rfloor -\lfloor {a\times 1+b\% c\over c}\rfloor=\lfloor{b\over c}\rfloor\)\(y\)

根據以上的分析,我們得到了

\[(n,a,b,c,x,y)=\underbrace{ yy\dots yy}_{\lfloor{b\over c}\rfloor}(n,a,b\%c,c,x,y)\quad b\ge c \]

再考慮\(a\ge c\)的情況,我們要使得\(a\to a\% c\)

此時\(f(x)=\lfloor{(a\%c )x+b\over c}\rfloor+\lfloor{a\over c}\rfloor x\),設\(f'(x)=\lfloor{(a\%c )x+b\over c}\rfloor\)\(g'\)\(f'\)的差分,我們發現\(g(i)=\lfloor {a\over c}\rfloor+g'(i)\),即任何一個\(y\)之前都緊貼着\(\lfloor{a\over c}\rfloor\)\(x\)

因此

\[(n,a,b,c,x,y)=(n,a\%c,b,c,x,\underbrace{xxx...xxx}_{\lfloor{a\over c}\rfloor}\space y) \quad a\ge c \]

解決問題

繼續沿用上面那個博客的思路,到了真正解決問題的時刻了

考慮現在我們只需要解決的問題是\(a,b<c\)

邊界情況:

關於\(a=0\)的情況退化爲一條平行於\(x\)的直線,就是填\(n\)\(y\)

\(b=0\)無所謂

\(a,b<c\)意味着一件事情,這個直線非常平緩,甚至填了若干個\(y\)才填一個\(x\)

這也意味着另一件事情,那就是一個\(x\)之前有若干個\(y\)

考慮 第\(j>1\)\(x\)之前有多少個\(y\),即考慮\(\max\limits_{m} \lfloor {am+b\over c}\rfloor< j\)。這即\(m=f'(j),j>1\)

解出來

\[m=\lfloor{jc-b-1\over a}\rfloor\quad j>1 \]

也就是從第二個\(x\)開始到第\(\lfloor{an+b\over c}\rfloor\)\(x\)是一個子問題(要記得令\(j=j-1\)並把\(c\)放出來,即)

\[f''(j)=\lfloor{jc+c-b-1\over a}\rfloor \quad j\ge 1 \]

而第一個\(x\)之前還有\(f''(0)\)\(y\),這一部分補上。

還有一個問題是,最後一個\(x\)之後可能還有\(y\)沒有統計到,我們手動算出來補在後面即可。

根據以上的分析,我們得到了

\[(n,a,b,c,x,y)=\underbrace{yyy...\dots y}_{\lfloor {c-b-1\over a}\rfloor}\ x \ (\lfloor{an+b\over c}\rfloor-1,c,c-b-1,a,y,x)\underbrace{yyy\dots yyy}_{n-\lfloor{c(\lfloor{an+b\over c}\rfloor-1)+(c-b-1)\over a}\rfloor} \quad a,b<c \]

總結

先把公式全寫出來

\[(n,a,b,c,x,y)= \begin{cases} \underbrace{ yy\dots yy}_{\lfloor{b\over c}\rfloor}(n,a,b\%c,c,x,y)& b\ge c \\ (n,a\%c,b,c,x,\underbrace{xxx...xxx}_{\lfloor{a\over c}\rfloor}\space y) & a\ge c \\ \underbrace{yyy...\dots y}_{\lfloor {c-b-1\over a}\rfloor}\ x \ (\lfloor{an+b\over c}\rfloor-1,c,c-b-1,a,y,x)\underbrace{yyy\dots yyy}_{n-\lfloor{c(\lfloor{an+b\over c}\rfloor-1)+(c-b-1)\over a}\rfloor} & \rm otherwise \end{cases} \]

然而求很多操作序列的疊加要用分治,是一個\(\log\)的,但是發現每次分治的長度是按除法遞減的,仔細分析一波發現就是\(\log n\)。(這裏地方太小我寫不下)

實現時注意每次調用遞歸時,要注意將第一個\(y\)之間的\(x\)補上($\rm case::otherwise $ \ 的補\(y\)也可以理解爲這個)。

這種寫法的一大好處是,遞歸部分完全不需要改動,需要改動的只有操作序列的合併方式。如果操作可以對應到矩陣那就完全不要改動了。

這種寫法的另一大好處是,任何良好定義的\(<op1(+),op2(\times )>\),且curB,curA的"迭代"有結合律和交換律,都可以套用這個做法。(泰拳警告:求多項式,超現實數,Floyd數組,SG函數,圖連通性的sum)

如果\(\sum\)後面是\(k\)個項相乘(但是有交換律)也能解決(我願稱之爲k階-類歐幾里得)

代碼實現上,由於\(case1\)\(case3\)的統一性,可以每次在遞歸調用前直接補上$\lfloor {b\over c}\rfloor $ ( \(case2\)除外),那麼每次進入一個遞歸馬上可以\(b\to b\%c\)

此外,注意到當\(f(1)=0\)的時候特殊處理下。

目前LOJ rk4 不知道爲啥我跑得這麼快qwq

//@winlere
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#define mod 998244353

typedef long long ll;
ll qr(){
	ll c=getchar(),ret=0,f=0;
	while(!isdigit(c)) f|=c==45,c=getchar();
	while( isdigit(c)) ret=ret*10+c-48,c=getchar();
	return ret;
}
int MOD(const int&x){return x>=mod?x-mod:x;}
int MOD(const int&x,const int&y){ll g=1ll*x*y;return g-g/mod*mod;}
int N;

struct EU{
	struct MAT{
		int v[20][20];
		MAT(){memset(v,0,sizeof(int)*400);}
		int*operator[](int x){return v[x];}
		MAT operator + (const MAT&x)const{
			MAT ret;
			for(int t=0;t<N;++t)
				for(int i=0;i<N;++i)
					ret[t][i]=MOD(v[t][i]+x.v[t][i]);
			return ret;
		}
		MAT operator * (const MAT&x)const{
			MAT ret;
			for(int k=0;k<N;++k)
				for(int t=0;t<N;++t)
					for(int i=0;i<N;++i)
						ret[t][i]=MOD(ret[t][i]+MOD(v[t][k],x.v[k][i]));
			return ret;
		}
	}sum,A,B;
	EU(const int&x=1):sum(),A(),B(){for(int t=0;t<N;++t) A[t][t]=B[t][t]=x;}
	EU operator + (const EU&x)const{
		EU ret;
		ret.A=A*x.A;
		ret.B=B*x.B;
		ret.sum=sum+A*x.sum*B;
		return ret;
	}
	EU operator * (const ll&x)const{
		EU ret,base=*this;
		for(ll t=x;t>0;t>>=1,base=base+base)
			if(t&1) ret=ret+base;
		return ret;
	}
};
ll f(ll x,ll a,ll b,ll c){return ((__int128)x*a+b)/c;}

EU solve(ll n,ll a,ll b,ll c,const EU&x,const EU&y){
	if(!n) return EU();
	b%=c;
	if(!f(n,a,b,c)) return y*n;
	if(a>=c) return solve(n,a%c,b,c,x,x*(a/c)+y);
	ll m=f(n,a,b,c),cnt=n-f(m-1,c,c-b-1,a);
	return y*((c-b-1)/a)+x+solve(m-1,c,c-b-1,a,y,x)+y*cnt;
}

int main(){
	ll a=qr(),c=qr(),b=qr(),n=qr(); N=qr();
	EU U,R;
	EU::MAT A,B;
	for(int t=0;t<N;++t)
		for(int i=0;i<N;++i)
			A[t][i]=qr();
	for(int t=0;t<N;++t)
		for(int i=0;i<N;++i)
			B[t][i]=qr();
	U.B=B; R.A=A; R.sum=A;
	EU::MAT ans=(U*(b/c)+solve(n,a,b,c,U,R)).sum;
	for(int t=0;t<N;++t,putchar('\n'))
		for(int i=0;i<N;++i)
			printf("%d ",ans[t][i]);
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章