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
using namespace std;
const int K=24;
int k,n,m;
long long f[1<>k>>n>>m;
for(int i=1,x;i<=n;++i){
string s;cin>>s,x=0;
for(int j=0;j>s,x=0;
for(int j=0;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>(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>(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>(2*j))&3)==0);
if(!cnt) printf("%lld\n",1ll*n*m-h[k&1][i]);
}
return 0;
}