题解 CF1336E2 【Chiori and Doll Picking (hard version)】
Fuyuki
2020-04-17 21:59:30
求出这 $n$ 个数的线性基 $A$,假设线性基的秩为 $k$。
为了简洁,用 $|x|$ 表示 $x$ 二进制下 $1$ 的个数,即 $\text{ popcount }(x)$。
定义 $F_c=\{x:|x|=c,x<2^m\},(x,y) =|x\& y|$。
原问题即可转化求成 $2^{n-k}\times \sum_{x\in F_c}[x\in A]$,下面先不考虑前面的常数项。
构造 $B=\{y:\forall x\in A,(x,y)\equiv 0\pmod 2\}$ ,则有:
$$
[x\in A]=\frac{1}{|B|}\sum_{y}(-1)^{(x,y)}[y\in B]
$$
$$
\sum_{x\in F_c}[x\in A]=\frac{1}{|B|}\sum_y[y\in B](\sum_{x\in F_c}(-1)^{(x,y)})
$$
考虑最后一项的取值:
$$
\sum_{x\in F_c}(-1)^{(x,y)}=\sum_{x\in F_c}\sum_{(x,y)=k}(-1)^k\binom{|y|}{k}\binom{m-|y|}{|x|-k}
$$
可以发现这个取值只和 $|y|$ 有关,因此可以预处理出 $w_{c,d}=\sum_{x\in F_c}(-1)^{(x,y)}(c=|x|,d=|y|)$ 。
接下来构造 $B$,一个构造方法是先将 $A$ 消元成每个主元列只有一个 $1$,然后转置并交换所有的主元和非主元。
![](https://cdn.luogu.com.cn/upload/image_hosting/1l0a8lag.png)
可以发现,对于 $A$ 中的一个基向量 $x$ 和 $B$ 中的一个基向量 $y$,只有当 $y$ 的主元对应到 $x$ 上的那一位为 $1$ 的时候才有 $(x,y)=2$,否则恒有 $(x,y)=0$。
因为 $|x\&z|+|y\&z|\equiv|(x\oplus y)\&z|\pmod 2$ ,而 $A$ 或 $B$ 能表示出的每个数都可以拆成若干的基向量的异或和,因此这就保证了 $(x,y)\equiv 0\pmod 2$ 对于 $x\in A,y\in B$ 恒成立。
并且,因为 $B$ 的每个主元列上只有一个 $1$,所以 $B$ 内的每个向量都线性无关,即 $B$ 是一个秩为 $m-k$ 的线性基。
那么可以在 $O(2^{m-k})$ 的时间内求出所有的 $y\in B$,结合预处理的 $w$ 就可以计算答案了。
如果 $k$ 较小,可以 $O(2^k)$ 枚举所有 $x\in A$ 并计算答案,如果 $k$ 较大,则可以按上述方法在 $O(2^{m-k})$ 的时间内计算答案。
取两者中较快的方法求解,即可达到 $O(nm+2^{m/2})$ 的总复杂度,可以通过全部的测试点。
P.S. 一组线性基对应一个线性空间,$B$ 就是 $A$ 在 $m$ 维空间中的正交,因为在这里每一维的取值都只有 $0,1$ 两种,所以 $B$ 是唯一的。(不唯一的时候,其余的正交空间都与 $B$ 线性相关)
```cpp
#include<bits/stdc++.h>
using namespace std;
#define I inline int
#define V inline void
#define ll long long int
#define cnt(x) __builtin_popcountll(x)
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define ROF(i,a,b) for(int i=a;i>=b;i--)
const int N=2e5+1,M=64,mod=998244353;
V check(int&x){x-=mod,x+=x>>31&mod;}
int n,k,m,tot;
ll a[N],f[M],g[M],t[M];
int ans[M],tmp[M],pw[N],c[M][M],w[M][M];
V ins(ll x){
ROF(i,m,0)if(x>>i&1){
if(f[i])x^=f[i];
else return f[i]=x,k++,void();
}
}
V dfs(int p,ll x){
if(p==tot)tmp[cnt(x)]++;
else dfs(p+1,x),dfs(p+1,x^t[p]);
}
int main(){
cin.tie(0),cin>>n>>m,pw[0]=1,m--;
FOR(i,1,n)cin>>a[i],ins(a[i]),check(pw[i]=pw[i-1]<<1);
FOR(i,0,m)FOR(j,0,i-1)if(f[i]>>j&1)f[i]^=f[j];
if(k<=26){
FOR(i,0,m)if(f[i])t[tot++]=f[i];
dfs(0,0);
FOR(i,0,m+1)cout<<1ll*pw[n-k]*tmp[i]%mod<<' ';
}
else{
FOR(i,0,m)FOR(j,0,m)g[j]|=(f[i]>>j&1)<<i;
FOR(i,0,m)if(g[i]^=1ll<<i)t[tot++]=g[i];
k=++m-k,dfs(0,0);
FOR(i,0,m)c[i][0]=1;
FOR(i,1,m)FOR(j,1,m)check(c[i][j]=c[i-1][j]+c[i-1][j-1]);
FOR(i,0,m)FOR(j,0,m)FOR(p,0,i)
if(p&1)check(w[i][j]+=mod-1ll*c[j][p]*c[m-j][i-p]%mod);
else check(w[i][j]+=1ll*c[j][p]*c[m-j][i-p]%mod);
FOR(i,0,m)FOR(j,0,m)
check(ans[i]+=1ll*tmp[j]*w[i][j]%mod);
int tag=pw[max(0,n-m+k)];
FOR(i,1,k)tag=1ll*(mod+1>>1)*tag%mod;
FOR(i,0,m)cout<<1ll*ans[i]*tag%mod<<' ';
}
return 0;
}
```