2024.11.8 鮮花

xrlong發表於2024-11-08
Moon Halo
Some deserts on this planet were oceans once
這顆星球上的一些沙漠曾是海洋
Somewhere shrouded by the night, the sun will shine
被黑夜籠罩的地方,也會迎來光明
Sometimes I see a dying bird fall to the ground
偶爾也會見到瀕死的鳥跌落地面
But it used to fly so high
但它也曾展翅高飛
I thought I were no more than a bystander till I felt a touch so real
我本以為我不過是個旁觀者 直到我感覺到如此真實的觸覺
I will no longer be a transient when I see smiles with tears
當我看到人們含淚的微笑 我便不再是個匆匆過客
If I have never known the sore of farewell and pain of sacrifices
如果不曾知曉生離死別的傷痛
What else should I engrave on my mind
我又該將什麼銘記於心
Frozen into icy rocks, that's how it starts
冰凍成石一般的開端
Crumbled like the sands of time, that's how it ends
沙漏崩落一般的終結
Every page of tragedy is thrown away burned out in the flame
悲劇的每一頁已被焚燒殆盡
A shoulder for the past
給過往一個肩膀
Let out the cries imprisoned for so long
讓久被禁錮的哭泣得以放聲
A pair of wings for me at this moment
給此刻的自己一雙翅膀
To soar above this world
翱翔於世界之上
Turn into a shooting star that briefly shines but warms up every heart
化為一顆流星,給每個心靈一瞬的希望
May all the beauty be blessed
願所有的美好都能得到祝福
May all the beauty be blessed
願所有的美好都能得到祝福
I will never go
我不會離開
There's a way back home
這就是我們的歸途
Brighter than tomorrow and yesterday
比過往與未來都要更加閃耀著
May all the beauty be blessed
願所有的美好都能得到祝福
Wave good-bye to the past when hope and faith have grown so strong and sound
當希望和信念羽翼豐滿,就向昨日告別吧
Unfold this pair of wings for me again
再一次為我張開這雙羽翼
To soar above this world
翱翔於世界之上
Turned into a moon that always tells the warmth and brightness of the sun
化為月亮長久地傳達著太陽的光耀
May all the beauty be blessed
願所有的美好都能得到祝福
May all the beauty be blessed
願所有的美好都能得到祝福

你說得對,沒人問我,但這是我們初一第一首起床鈴

sosdp,FMT,FWT 下

不是,怎麼 FMT 只有板子和不可做題啊!!!

那就先放板子吧:CF582E Boolean Function

solution

起手建表示式樹。

\(A,B,C,D\) 只有四個,直接狀壓,設 \(dp_{i,j}\) 表示在 \(i\) 的子樹內,狀態為 \(j\) 的方案數,轉移就是 OR 和 AND 卷積。

但是隊奶告訴我可以 FMT 可以直接求異或卷積,快快推薦一下。

感覺放課件太魔怔了,想看的可以看 this,就複述一下把。

考慮字首和時第 \(i\) 維實際上是由兩個第 \(i-1\) 維組成的,設其字首和完 \(i-1\) 維的值為 \(a_i,b_i\),那麼字首和實際上就是變成了 \(a_i,b_i+a_i\)

類似定義新運算,依次對第 \(i\) 維做 \(a_i+b_i,a_i-b_i\)

逆運算是顯然的 \(\frac{a_i+b_i}2,\frac{a_i-b_i}2\)

於是我們用這個做莫變即可。

證明:\((a_i,b_i)*(c_i,d_i)\to(a_i+b_i,a_i-b_i)(c_i+d_i,c_i-d_i)=(a_ic_i+a_id_i+b_ic_i+b_id_i,a_ic_i-a_id_i-b_ic_i+b_id_i)\to(a_ic_i+b_id_i,a_id_i+b_ic_i)\)

於是就有真正的板子了:P4717 【模板】快速莫比烏斯/沃爾什變換 (FMT/FWT)

放個程式碼:

Code
#include<bits/stdc++.h>
using namespace std;
using llt=long long;
using llf=long double;
using ull=unsigned long long;
#define endl '\n'
#ifdef LOCAL
	FILE *InFile=freopen("in_out/in.in","r",stdin),*OutFile=freopen("in_out/out.out","w",stdout);
#else
	FILE *InFile=stdin,*OutFile=stdout;
#endif

const int N=1<<17|3,MOD=998244353,Inv=(MOD+1)/2;
int Add(int a,int b){return (a+=b)>=MOD?a-MOD:a;}
int Sub(int a,int b){return (a-=b)<0?a+MOD:a;}
int ca[N],cb[N],a[N],b[N],n;

void Fmt(int *f,const function<void(int &,int &)> &F){for(int i=1;i<n;i<<=1) for(int j=0;j<n;++j) if(j&i) F(f[j^i],f[j]);}
const function<void(int &,int &)>  
	Or=[](int &a,int &b){b=Add(a,b);},
	And=[](int &a,int &b){a=Add(a,b);},
	Xor=[](int &a,int &b){int t=a; a=Add(a,b),b=Sub(t,b);};
void Cp(){memcpy(a,ca,sizeof a),memcpy(b,cb,sizeof b);}
void Mrg(){for(int i=1;i<=n;++i) a[i]=1ll*a[i]*b[i]%MOD;} 
void Out(){for(int i=1;i<=n;++i) cout<<a[i]<<' '; cout<<endl;}

int main(){
	ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
	cin>>n; n=1<<n; for(int i=1;i<=n;++i) cin>>ca[i]; for(int i=1;i<=n;++i) cin>>cb[i];
	Cp(),Fmt(a+1,Or),Fmt(b+1,Or),Mrg(),Fmt(a+1,[](int &a,int &b){b=Sub(b,a);}),Out();
	Cp(),Fmt(a+1,And),Fmt(b+1,And),Mrg(),Fmt(a+1,[](int &a,int &b){a=Sub(a,b);}),Out();
	Cp(),Fmt(a+1,Xor),Fmt(b+1,Xor),Mrg(),Fmt(a+1,[](int &a,int &b){int t=a; a=1ll*(a+b)*Inv%MOD,b=1ll*(t-b+MOD)*Inv%MOD;}),Out();
}

但只會這個還不夠,你還要會寫子集卷積。

\(h(S)=\sum_{T\subseteq S} f(T)g(S-T)\)

你發現這個相較於 OR 卷積多出了一個 \(A\cap B = \varnothing\)

發現 \(A\cap B = \varnothing\Leftrightarrow |A|+|B|=|A \cup B|\)

所以如果我們每次只考慮大小為 \(p\) 的集合,將所的函式記為 \(f_p\),有 \(h_{i+j}(S)=\sum_{T\subseteq S} f_i(T)g_j(S-T)\)

所以對每一個 \(p\) 單獨計算最後在暴力卷積即可,複雜度 \(n^2\log n\)\(n\log^2 n\)

板子:P6097 【模板】子集卷積

Code
#include<bits/stdc++.h>
using namespace std;
using llt=long long;
using llf=long double;
using ull=unsigned long long;
#define endl '\n'
#ifdef LOCAL
	FILE *InFile=freopen("in_out/in.in","r",stdin),*OutFile=freopen("in_out/out.out","w",stdout);
#else
	FILE *InFile=stdin,*OutFile=stdout;
#endif

const int N=1<<20|3,MOD=1e9+9,Inv=(MOD+1)/2;
int Add(int a,int b){return (a+=b)>=MOD?a-MOD:a;}
int Sub(int a,int b){return (a-=b)<0?a+MOD:a;}
int n,ca[23][N],cb[23][N],aas[23][N];

void Fmt(int *f,const function<void(int &,int &)> &T){for(int i=0;i<n;++i) for(int j=0;j<1<<n;++j) if(j>>i&1) T(f[j^1<<i],f[j]);}
const function<void(int &,int &)> Or=[](int &a,int &b){b=Add(a,b);},IOr=[](int &a,int &b){b=Sub(b,a);};

int main(){
	ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
	cin>>n; for(int i=0;i<1<<n;++i) cin>>ca[__builtin_popcount(i)][i]; for(int i=0;i<1<<n;++i) cin>>cb[__builtin_popcount(i)][i];
	for(int i=0;i<=n;++i) Fmt(ca[i],Or),Fmt(cb[i],Or);
	for(int i=0;i<=n;++i) for(int j=0;j<=i;++j) for(int s=0;s<1<<n;++s) aas[i][s]=Add(aas[i][s],1ll*ca[j][s]*cb[i-j][s]%MOD);
	for(int i=0;i<=n;++i) Fmt(aas[i],IOr); for(int i=0;i<1<<n;++i) cout<<aas[__builtin_popcount(i)][i]<<' ';
}

FWT

其實 FWT 和 FMT 本質是一個東西,它們倆變換出來的多項式都一模一樣。

上次也說了,FWT 是基於變換的思想實現的,和 FFT 更相似,也更難寫,所以學這個不是為了寫板子的,其實是在學一些性質和構造本質的東西。

可以先去模板題的題解看一下板子。

容易發現,這個東西是線性變換,所以有 \(FWT(A+B)=FWT(A)+FWT(B),FWT(cA)=cFWT(A)\)

以下的 \(i,j\) 都表示狀壓後的狀態。

我們設,對於第 \(i\) 個元素,他會給每個 \(j\in[1,n]\) 貢獻 \(c(i,j)\) 次,其實用矩陣乘法表示就是那個矩陣。

容易發現,對於 OR 卷積,\(c(i,j)=[i\&j==i]\),AND 卷積,\(c(i,j)=[i\&j==i]\),XOR 卷積,\(c(i,j)=(-1)^{popcount(i\&j)}\)

知道了上面的,來做好題:CF1119H Triple

solution

首先暴力卷積是顯然的,考慮以 \(a,b,c\) 為指數,\(x,y,z\) 為對應係數,直接做異或卷積即可,複雜度 \(nk2^k\),過不去。

發現只有三個位置有值,對於每個的莫變可以暴力乘上貢獻次數,這樣的複雜度是 \((n+k)2^k\),還是過不去。

發現最後的卷積陣列 \(S\) 的第 \(i\)\(S_i=\prod\limits_{k=1}^n c(k,a_k)x+c(k,b_k)y+c(k,c_k)z\)

後面的取值只有 \(8\) 種可能,如果你有耐心,依據接下來的規律找出 \(8\) 種次數的 \(8\) 個方程也是不難做到的,但是可以減少情況。

考慮將 \(a,b,c\)\(\oplus a\),最後查詢時也 \(\oplus a\),這樣 \(c(k,a_k)=c(k,0)=1\),所以只有 \(4\) 種情況。

考慮設 \(x+y+z,x+y-z,x-y+z,x-y-z\) 的次數分別為 \(t_1,t_2,t_3,t_4\),考慮解方程,顯然的第一個方程是 \(t_1+t_2+t_3+t_4=n\)

發現 \(y\)\(z\) 的符號互相無關,考慮 \(y\) 的係數,將 \(x=0,y=1,z=0\) 帶入變換,得到的結果為 \(FMT(G_i)\),則有 \(\sum FMT(G_i)=\sum c(k,b_k)=t_1+t_2-t_3-t_4\),計算 \(\sum FMT(G_i)\) 是容易的,發現 FMT 是線性變換,求 \(FMT(G'[k]=\sum[b_k=k])\) 即可。

對於 \(z\) 做類似處理可以在得到一個方程,考慮最後一個方程,發現其應該由 \(y,z\) 共同給出,於是對於每個 \(i\)\(F_i[b_i\oplus c_i]=1\)(這裡 \(F[k]\) 表示多項式 \(F\)\(k\) 次係數,前面的兩種帶入可以視為 \(G_i=F_i[b_i]=1\)\(G_i=F_i[c_i]=1\) ),容易發現其卷積結果是 \(\sum c(i,b_i)c(i,c_i)=t_1-t_2-t_3+t_4\)

解方程即可,最後做一遍 IFMT 即可,複雜度 \(O((k+\log n)2^k)\)

Code
#include<bits/stdc++.h>
using namespace std;
using llt=long long;
using llf=long double;
using ull=unsigned long long;
#define endl '\n'
#ifdef LOCAL
	FILE *InFile=freopen("in_out/in.in","r",stdin),*OutFile=freopen("in_out/out.out","w",stdout);
#else
	FILE *InFile=stdin,*OutFile=stdout;
#endif

const int N=2e5+3,MOD=998244353,Inv=(MOD+1)/2;
int n,m,x,y,z,p1[N],p2[N],p3[N],aas[N],sa;

int Fpw(int a,int b){
	int ans=1;
	while(b){
		if(b&1) ans=1ll*a*ans%MOD;
		a=1ll*a*a%MOD,b>>=1;
	}
	return ans;
}
void Fmt(int *f,const function<void(int &,int &)> &F){for(int i=1;i<m;i<<=1) for(int j=0;j<m;++j) if(j&i) F(f[j^i],f[j]);}
const auto Xor=[](int &a,int &b){int t=a; a=(a+b)%MOD,b=(t-b)%MOD;};
const auto IXr=[](int &a,int &b){int t=a; a=1ll*(a+b)*Inv%MOD,b=1ll*(t-b)*Inv%MOD;};

int main(){
	ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
	cin>>n>>m>>x>>y>>z,m=1<<m; for(int i=1;i<=n;++i){int a,b,c; cin>>a>>b>>c; sa^=a,b^=a,c^=a,++p1[b],++p2[c],++p3[b^c];}
	Fmt(p1,Xor),Fmt(p2,Xor),Fmt(p3,Xor); int s1=(1ll*x+y+z)%MOD,s2=(1ll*x+y-z)%MOD,s3=(1ll*x-y+z)%MOD,s4=(1ll*x-y-z)%MOD;
	for(int i=0;i<m;++i){
		int c1=(1ll*n+p1[i]+p2[i]+p3[i])/4,
			c2=(1ll*n+p1[i]-c1-c1)/2,
			c3=(1ll*n+p2[i]-c1-c1)/2,
			c4=(1ll*n+p3[i]-c1-c1)/2;
		aas[i]=1ll*Fpw(s1,c1)*Fpw(s2,c2)%MOD*Fpw(s3,c3)%MOD*Fpw(s4,c4)%MOD;
	}
	Fmt(aas,IXr); for(int i=0;i<m;++i) cout<<(aas[i^sa]+MOD)%MOD<<' ';
}

擴充套件到 \(k\) 進位制

對於取 \(max,min\) 可以直接套 \(FMT\),因為其本質是前字尾和,但是不進位加法涉及一些深刻矛盾,我還不會,等我會了在寫。

P