题解 食材

传送门

这都哪里来的神仙题

首先有个暴力 \(\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)\),证明很容易只是不容易想到

于是就可以推下式子:

\[\begin{aligned}&[x^q]\operatorname{FWT}(\sum\limits_{i=1}^nx^{b_i}) \\&=\sum\limits_{i=1}^n(-1)^{|b_i\&q|} \\&=\sum\limits_{i=1}^n\prod\limits_{j\in T}(-1)^{|a_{i, j}\&q|} \\&=\sum\limits_{s=0}^{2^k-1}cnt_{q,s}(-1)^{|s\&T|} \\&=[x^T]\operatorname{FWT}(\sum\limits_{i=0}^{2^k-1}cnt_{q, i}x^i) \end{aligned} \]

\(\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;
}
posted @ 2022-02-24 06:13  Administrator-09  阅读(2)  评论(0编辑  收藏  举报