题解 食材
这都哪里来的神仙题
首先有个暴力 \(\operatorname{FWT}\) 的做法
虽然求和上界是 \(k\),但是 \(x^{a_{i, j}}\) 中 \(a_{i, j}\in[0, 2^m-1]\),所以 FWT 的下标范围还是 \([0, 2^m-1]\)
虽然下标范围是 \([0, 2^m-1]\),但是由岐由其在本题中的定义式可知最多只有 \(k\) 项是有值的
令 \(f_i=\sum\limits_{j=1}^kw_jx^{a_{i, j}}\) 为第 \(i\) 种食材形成的集合幂级数
那么 \(\operatorname{FWT}_i=\sum\limits_{j=1}^ks_jw_j\) 中的系数集合 \(s\) 只有 \(2^k\) 种可能的取值,所以可以状压一下
特别注意,这里指的是 \(w_1,\cdots,w_k\) 而不是位置 \(1,\cdots,2^m-1\) 对位置 \(i\) 的贡献系数
而我们最终的要做的事是对于每个 \(i\),求 \(\prod\limits_{j=1}^n[x^i]\operatorname{FWT}_j\)
那么一种可能的优化方法是对于一个 \(i\),枚举一个可能的系数集合 \(s\),求出有多少个 \(j\) 满足系数集合为 \(s\)
那么这时快速幂乘一下,最后 \(\operatorname{IFWT}\) 回去就好了
所以怎么求有多少个 \(j\) 满足系数集合为 \(s\) 呢?
link
link
link
有个疑似套路的做法是找等量关系列方程
那么我们先状压掉贡献状态
具体地,我们令 \(cnt_{i, j}\) 为 \(\operatorname{FWT}\) 中第 \(i\) 个点位上贡献状态为 \(j\) 的数量
尝试对 \(cnt_{i, 0},\cdots,cnt_{i, 2^k-1}\) 列出一些方程来
一些引入上面博客里有,这边直接跳过了
考虑枚举 \(\{1,\cdots,k\}\) 的一个子集 \(T\),令 \(b_i=\oplus_{j\in T}a_{i, j}\)
- \(\sum\limits_{i=1}^n\operatorname{FWT}(x^{a_i})=\operatorname{FWT}(\sum\limits_{i=1}^nx^{a_i})\)
证明的话我好像只会大力展开 - \(\oplus_{i=1}^n\operatorname{popcount}(a_i\&k)=\operatorname{popcount}(\oplus_{i=1}^na_i\&k)\),证明很容易只是不容易想到
于是就可以推下式子:
\(\rm{IFWT}\) 一下就可以解出 \(cnt\) 了
复杂度大约 \(O(nk2^k+2^m(k2^k+k))\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, m, k;
ll tem[1<<16], cnt[1<<16][1<<6], ans[1<<16];
int rev[N], w[N], a[N][6];
const ll mod=998244353;
inline ll qpow(ll a, ll b) {ll ans=1; for (; b; a=a*a%mod,b>>=1) if (b&1) ans=ans*a%mod; return ans;}
void fwt(ll* a, int len, int op) {
ll x, y, inv2=qpow(2, mod-2);
for (int i=1; i<len; i<<=1) {
for (int j=0,step=i<<1; j<len; j+=step) {
for (int k=j; k<j+i; ++k) {
x=a[k]; y=a[k+i];
a[k]=(x+y)%mod;
a[k+i]=(x-y)%mod;
if (op==-1) a[k]=a[k]*inv2%mod, a[k+i]=a[k+i]*inv2%mod;
}
}
}
}
signed main()
{
freopen("lee.in", "r", stdin);
freopen("lee.out", "w", stdout);
n=read(); m=read(); k=read();
for (int i=0; i<k; ++i) w[i]=read();
for (int i=1; i<=n; ++i) for (int j=0; j<k; ++j) a[i][j]=read();
for (int s=0; s<(1<<k); ++s) {
memset(tem, 0, sizeof(tem));
for (int i=1,t=0; i<=n; ++i,t=0) {
for (int j=0; j<k; ++j) if (s&(1<<j)) t^=a[i][j];
++tem[t];
}
fwt(tem, 1<<m, 1);
for (int q=0; q<(1<<m); ++q) cnt[q][s]=tem[q];
}
// cout<<"cnt"<<endl; for (int s=0; s<(1<<m); ++s) {for (int i=0; i<(1<<k); ++i) cout<<cnt[s][i]<<' '; cout<<endl;}
for (int q=0; q<(1<<m); ++q) {
ans[q]=1;
fwt(cnt[q], 1<<k, -1);
// cout<<"cnt"<<q<<": "; for (int s=0; s<(1<<k); ++s) cout<<cnt[q][s]<<' '; cout<<endl;
for (int s=0; s<(1<<k); ++s) {
ll sum=0;
for (int i=0; i<k; ++i)
if (s&(1<<i)) sum=(sum-w[i])%mod;
else sum=(sum+w[i])%mod;
ans[q]=ans[q]*qpow(sum, cnt[q][s])%mod;
// cout<<"sum: "<<sum<<endl;
}
// printf("%lld ", (ans%mod+mod)%mod);
}
// cout<<"ans: "; for (int i=0; i<(1<<m); ++i) cout<<ans[i]<<' '; cout<<endl;
fwt(ans, 1<<m, -1);
for (int i=0; i<(1<<m); ++i) printf("%lld ", (ans[i]%mod+mod)%mod);
printf("\n");
return 0;
}