1. 程式人生 > 其它 >ARC132F Takahashi The Strongest

ARC132F Takahashi The Strongest

感覺自己對於高維字首和的理解有點問題。

我們一般認為的高維字首和基本是在二進位制下運作的,但是實際上可以運用地更加廣泛。

比如我們定義一個四進位制數的運算,對於每一位 \(x\oplus y=\begin{cases} x&x=y\\0&x\ne y\end{cases}\) ,實際上這個東西也是可以進行高位字首和的。具體的實踐方式就是將子集的定義修改一下,定義為 \(0\sub 1,0\sub 2,0\sub 3\) ,而 \(1,2,3\) 兩兩互不包含。

我們考慮上面的這種形式實際上就是一個 \(3\times \text{len}\) 的二進位制高位字首和,但是由於每三位中的數不會出現兩個及以上,所以我們可以利用這樣的方式壓縮。

考慮這道題目實際上就是已經可以利用這種方式來算了,我們將 \(1,2,3\) 分別定義為 P, R, S ,同時我們將自己的贏法修改一下,改為和他們出的一樣,那麼顯然的,將他們兩者進行一個類 \(\text{fwt}\) 的變換,我們就可以得到他們在哪些位置同時出兩個相同的概率。

已知各種情況下的概率時,我們需要計算出哪些和其存在至少一個相同的位置能夠獲得貢獻。至少一個相同不好搞,我們取其反面不存在任何一個位置相同,這個東西就是情況取反之後的子集,我們考慮我們實際上也是可以做一個類似於高維字首和的東西來處理的,但是實際上這個東西更加類似於 \(\text{dp}\) ,用 \(F_{i,S}\)

表示集合為 \(S\) 且前 \(i\) 為取反,剩下位沒有取反的概率。


myy 指導了一下關於高維字首和的相關事項,實際上就是類似於 \(\text{fwt}\) 的,每次取相鄰的四個,再取隔三個的相鄰的四個……實際上兩者是等價的。

#include<bits/stdc++.h>
using namespace std;
const int K=24;
int k,n,m;
long long f[1<<K],g[1<<K],h[2][1<<K],res=0;
int id(char c){return c=='R'?1:(c=='S'?2:3);}
int main(){
	cin>>k>>n>>m;
	for(int i=1,x;i<=n;++i){
		string s;cin>>s,x=0;
		for(int j=0;j<k;++j) x+=(1<<(2*j))*id(s[k-j-1]);
		f[x]++;
	}
	for(int i=1,x;i<=m;++i){
		string s;cin>>s,x=0;
		for(int j=0;j<k;++j) x+=(1<<(2*j))*id(s[k-j-1]);
		g[x]++;
	}
	for(int i=0;i<k;++i){
		for(int j=(1<<(2*k))-1;j>=0;--j){
			int tmp=((j>>(2*i))&3);if(!tmp) continue;
			f[j-(tmp<<(2*i))]+=f[j],g[j-(tmp<<(2*i))]+=g[j];
		}
	}
	for(int i=0;i<(1<<(2*k));++i) f[i]*=g[i];
	for(int i=0;i<k;++i){
		for(int j=0;j<(1<<(2*k));++j){
			int tmp=((j>>(2*i))&3);if(!tmp) continue;
			f[j-(tmp<<(2*i))]-=f[j];
		}
	}
	for(int i=0;i<(1<<(2*k));++i) h[0][i]=f[i];
	for(int i=0;i<k;++i){
		for(int j=0;j<(1<<(2*k));++j) h[(i+1)&1][j]=0;
		for(int j=0;j<(1<<(2*k));++j){
			int tmp=((j>>(2*i))&3),J=j-(tmp<<(2*i));
			h[(i+1)&1][J]+=h[i&1][j];
			if(tmp!=1) h[(i+1)&1][J+(1<<(2*i))]+=h[i&1][j];
			if(tmp!=2) h[(i+1)&1][J+(2<<(2*i))]+=h[i&1][j];
			if(tmp!=3) h[(i+1)&1][J+(3<<(2*i))]+=h[i&1][j];
		}
	}
	for(int i=0;i<(1<<(2*k));++i){
		int cnt=0;
		for(int j=0;j<k;++j) cnt+=(((i>>(2*j))&3)==0);
		if(!cnt) printf("%lld\n",1ll*n*m-h[k&1][i]);
	}
	return 0;
}