ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

NFLSOJ 1072 - 【2021 六校联合训练 NOIP #1】异或(FWT+插值)

2021-08-25 09:02:49  阅读:230  来源: 互联网

标签:六校 NFLSOJ limits int sum 1072 1ll MAXN MOD


题面传送门

一道非常不错的 FWT+插值的题 %%%%%%%%%%%%

还是那句话,反正非六校的看不到题对吧(((

方便起见在下文中设 \(n=2^d\)。

首先很明显的一点是这题涉及两个维度:异或和与选出的元素的个数。因此考虑像子集卷积那样建立一个二元生成函数表示这个东西,具体来说我们定义一个幂级数 \(F\)​​ 形如 \(\sum\limits_{n\ge 0}\sum\limits_{m\ge 0}f_{n,m}x^ny^m\)​​,并定义两个幂级数 \(F,G\)​​ 的乘法得到的幂级数 \(H\)​​ 满足 \(H_{i,j}=\sum\limits_{p\oplus q=i}\sum\limits_{r+s=j}F_{p,r}G_{q,s}\)​​,那么这样我们相当于将所有 \(F_i(x,y)=1+a_ix^{i}y\)​​ 乘起来,具体来说,设 \(G=\prod\limits_{i=0}^{n-1}1+a_ix^iy\)​​,那么 \(res_i=\sum\limits_{j=0}^{n-1}[x^jy^i]G·b_j\)​,或者如果我们将答案的生成函数视作一个整体 \(R(x)\),那么 \(R(x)=\sum\limits_{j=0}^{n-1}[x^j]G·b_j\)。

考虑怎么求解 \(G\),按照子集卷积的套路我们考虑定某个东西为主元,在这题中我们考虑定 \(x\) 为主元,那么所有集合幂级数 \(F\) 都可以写成 \(\sum\limits_{i=0}^{n-1}F_i(y)x^i\),其中 \(F_i(y)\) 为某个关于 \(y\) 的多项式。这样一来两个幂级数做乘法则相当于对两个以形式幂级数为系数的集合幂级数做 xor 卷积,因此考虑将每个集合幂级数都 FWTxor 一遍,对应项相乘再 IFWTxor 回去即可得到上文中所说的 \(G\)。具体来说,显然 \(\text{FWT}(F_i(x,y))=\sum\limits_{j=0}^{n-1}(1+(-1)^{\text{builtin\_popcount}(i\&j)}a_iy)·x^j\)​,因此对应项相乘就有 \(\text{FWT}(G)=\sum\limits_{j=0}^{n-1}(\prod\limits_{i=0}^{n-1}(1+(-1)^{\text{builtin\_popcount}(i\&j)}a_iy))·x^j\),再 IFWTxor 回去即可算出真正的 \(G\),进而求出答案。

但是这样暴力操作复杂度还是会出问题,考虑优化。首先注意到 IFWTxor 是个线性变换,因此我们考虑不将 IFWTxor 作用与 \(\text{FWT}(G)\),instead 我们将其作用于 \(b\)​,也就是说答案的多项式 \(R(x)\) 可以写成 \(\sum\limits_{j=0}^{n-1}[x^j]G·b'_j\),其中 \(b’=\text{IFWT}(b)\),即

\[R(x)=\sum\limits_{j=0}^{n-1}b'_j\prod\limits_{i=0}^{n-1}(1+(-1)^{\text{builtin\_popcount}(i\&j)}a_ix) \]

考虑怎么求解后面那个东西,我们考虑用类似于 FWTxor 的东西求解。考虑分治,具体来说当我们求一排这样的多项式的乘积时候将序列分成左右两部分 \(F_1(x),F_2(x)\),那么合并时候,对于所有 \(i\in[0,\dfrac{n}{2})\),仿照 FWTxor 的套路则有 \(F_i(x)=F_{1,i}(x)F_{2,i}(x),F_{i+n/2}(x)=F_{1,i}(x)F_{2,i}(-x)\),如果直接莽个 MTT 上去复杂度是 \(d^24^d\) 的,并且自带大常数,无法通过,不过注意到最后答案序列也是一个 \(n\) 次多项式,因此考虑插值。具体来说,我们使用上述方法求出以上 \(n\) 个多项式在 \(-\dfrac{n}{2},-\dfrac{n}{2}+1,\cdots,0,\cdots,\dfrac{n}{2}-1,\dfrac{n}{2}\) 处的点值,这样可以在 \(\mathcal O(n^2)\) 时间内计算出 \(R(x)\) 在 \(-\dfrac{n}{2},-\dfrac{n}{2}+1,\cdots,0,\cdots,\dfrac{n}{2}-1,\dfrac{n}{2}\),再一遍拉格朗日插值即可得到 \(R(x)\) 的系数。

时间复杂度 \(4^d·d\)

const int MAXN=2052;
const int MOD=1e9+7;
const int INV2=5e8+4;
int n,d,a[MAXN+5],b[MAXN+5],sum[MAXN+5];
int val[MAXN+5][MAXN+5],tmp[MAXN+5][MAXN+5];
void FWTxor(int *a,int len,int type){
	for(int i=2;i<=len;i<<=1)
		for(int j=0;j<len;j+=i)
			for(int k=0;k<(i>>1);k++){
				int X=a[j+k],Y=a[(i>>1)+j+k];
				a[j+k]=1ll*type*(X+Y)%MOD;
				a[(i>>1)+j+k]=1ll*type*(X-Y+MOD)%MOD;
			}
}
int prd[MAXN+5],dv[MAXN+5];
int inv[MAXN+5],ss[MAXN+5];
int getinv(int x){return (x<0)?(MOD-inv[-x]):inv[x];}
void init_fac(int n){
	for(int i=(inv[0]=inv[1]=1)+1;i<=n;i++) inv[i]=1ll*inv[MOD%i]*(MOD-MOD/i)%MOD;
}
int main(){
	scanf("%d",&d);n=1<<d;init_fac(MAXN);
	for(int i=0;i<n;i++) scanf("%d",&a[i]);
	for(int i=0;i<n;i++) scanf("%d",&b[i]);
	FWTxor(b,n,INV2);
	for(int i=0;i<n;i++) for(int j=-n>>1;j<=(n>>1);j++)
		val[i][j+(n>>1)]=(1+1ll*a[i]*j%MOD+MOD)%MOD;
	for(int i=2;i<=n;i<<=1){
		memset(tmp,0,sizeof(tmp));
		for(int j=0;j<n;j+=i)
			for(int k=0;k<(i>>1);k++)
				for(int x=-n>>1;x<=(n>>1);x++){
					tmp[j+k][x+(n>>1)]=1ll*val[j+k][x+(n>>1)]*val[(i>>1)+j+k][x+(n>>1)]%MOD;
					tmp[(i>>1)+j+k][x+(n>>1)]=1ll*val[j+k][x+(n>>1)]*val[(i>>1)+j+k][-x+(n>>1)]%MOD;
				}
		for(int j=0;j<n;j++) for(int k=0;k<=n;k++)
			val[j][k]=tmp[j][k];
	}
	for(int j=0;j<n;j++) for(int k=0;k<=n;k++)
		sum[k]=(sum[k]+1ll*val[j][k]*b[j])%MOD;
//	for(int k=0;k<=n;k++) printf("%d%c",sum[k]," \n"[k==n]);
	prd[0]=1;
	for(int x=-n>>1;x<=(n>>1);x++){
		for(int i=n+1;i;i--)
			prd[i]=(0ll+prd[i-1]+1ll*prd[i]*x%MOD+MOD)%MOD;
		prd[0]=1ll*prd[0]*(x+MOD)%MOD;
	}
//	for(int i=0;i<=n+1;i++) printf("%d%c",prd[i]," \n"[i==n+1]);
	for(int v=-n>>1;v<=(n>>1);v++){
		memset(dv,0,sizeof(dv));
		for(int i=n;~i;i--)
			dv[i]=(0ll+prd[i+1]-1ll*(MOD-v)*dv[i+1]%MOD+MOD)%MOD;
		int mul=1;
		for(int x=-n>>1;x<=(n>>1);x++) if(v^x)
			mul=1ll*mul*getinv(v-x)%MOD;
		mul=1ll*mul*sum[v+(n>>1)]%MOD;
//		printf("%d %d\n",v,mul);
//		for(int i=0;i<=n;i++) printf("%d%c",dv[i]," \n"[i==n]);
		for(int i=0;i<=n;i++) ss[i]=(ss[i]+1ll*dv[i]*mul)%MOD;
	} for(int i=1;i<=n;i++) printf("%d%c",ss[i]," \n"[i==n]);
	return 0;
}
/*
2
1 2 3 4
0 1 2 3
*/

标签:六校,NFLSOJ,limits,int,sum,1072,1ll,MAXN,MOD
来源: https://www.cnblogs.com/ET2006/p/nflsoj-1072.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有