Newcoder 147 A.Circulant Matrix(FWT+递归)

本文介绍了一种解决特定类型的矩阵同余方程的方法,该方程涉及到异或运算。通过利用快速沃尔什-哈达玛变换(FWT)简化求解过程,实现了高效的算法设计。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

给出两个长度为nnn的序列a0,...,an−1a_0,...,a_{n-1}a0,...,an1b0,...,bn−1b_0,...,b_{n-1}b0,...,bn1,以此定义矩阵Ai,j=ai⊙jA_{i,j}=a_{i\odot j}Ai,j=aij,其中⊙\odot为异或运算,求解矩阵同余方程Ax=b(mod p)Ax=b(mod\ p)Ax=b(mod p)

Input

第一行一整数nnn表示序列长度,之后输入两个长度为nnn的序列ai,bia_i,b_iai,bi

(1≤n≤222144,p=109+7,0≤ai,bi&lt;p)(1\le n\le 222144,p=10^9+7,0\le a_i,b_i&lt;p)(1n222144,p=109+7,0ai,bi<p)

Output

保证有唯一解,输出x0,...,xn−1x_0,...,x_{n-1}x0,...,xn1,需保证0≤xi&lt;p0\le x_i&lt;p0xi<p

Sample Input

4
1 10 100 1000
1234 2143 3412 4321

Sample Output

4
3
2
1

Solution1

Ai,j=ai⊙jA_{i,j}=a_{i\odot j}Ai,j=aij,原矩阵方程等价于方程组∑j=1nai⊙jxj=bi\sum\limits_{j=1}^n a_{i\odot j}x_j=b_ij=1naijxj=bi,也即∑j⊙k=iajxk=bi\sum\limits_{j\odot k=i}a_jx_k=b_ijk=iajxk=bi,这意味着a⊗x=ba\otimes x=bax=b,其中⊙\odot表示异或运算,⊗\otimes表示异或卷积,那么有FWT(a)⋅FWT(x)=FWT(b)FWT(a)\cdot FWT(x)=FWT(b)FWT(a)FWT(x)=FWT(b),其中⋅\cdot表示点乘,故对a,ba,ba,b序列做FWTFWTFWT后得到bi⋅ai−1b_i\cdot a_i^{-1}biai1序列再UFWTUFWTUFWT即得到xxx序列

Code1

#include<cstdio>
using namespace std;
typedef long long ll;
#define maxn 262144+5
#define mod 1000000007
#define inv2 500000004
int mul(int x,int y)
{
	ll z=1ll*x*y;
	return z-z/mod*mod;
}
int add(int x,int y)
{
	x+=y;
	if(x>=mod)x-=mod;
	return x;
}
int Pow(int a,int b)
{
	int ans=1;
	while(b)
	{
		if(b&1)ans=mul(ans,a);
		a=mul(a,a);
		b>>=1;
	}
	return ans;
}
int n,a[maxn],b[maxn],x[maxn];
void FWT(int *a,int n)  
{  
    for(int d=1;d<n;d<<=1)  
        for(int i=0;i<n;i+=(d<<1))  
            for(int j=0;j<d;j++)  
	            {  
	                int x=a[i+j],y=a[i+j+d];  
	                a[i+j]=add(x,y),a[i+j+d]=add(x,mod-y);   
	            } 
}   
void UFWT(int *a,int n)  
{  
    for(int d=1;d<n;d<<=1)  
        for(int i=0;i<n;i+=(d<<1))  
            for(int j=0;j<d;j++)  
            {  
                int x=a[i+j],y=a[i+j+d];  
                a[i+j]=mul(inv2,add(x,y)),a[i+j+d]=mul(inv2,add(x,mod-y));   
            }  
} 
int main()
{
	scanf("%d",&n);
	for(int i=0;i<n;i++)scanf("%d",&a[i]);
	for(int i=0;i<n;i++)scanf("%d",&b[i]);
	FWT(b,n),FWT(a,n);
	for(int i=0;i<n;i++)x[i]=mul(b[i],Pow(a[i],mod-2));
	UFWT(x,n);
	for(int i=0;i<n;i++)printf("%d\n",x[i]);
	return 0;
}

Solution2

考虑i⊙ji\odot jij矩阵,由于nnn222的整数次幂,简单分析可知AAA矩阵可以分块用两个n2⋅n2\frac{n}{2}\cdot \frac{n}{2}2n2n的矩阵表示
A=[A1 A2A2 A1] A= \left[\begin{matrix} A^1\ A^2\\ A^2\ A^1 \end{matrix}\right] A=[A1 A2A2 A1]
同时将x,bx,bx,b分成前后两个等长的部分,x=[x1,x2]T,b=[b1,b2]Tx=[x^1,x^2]^T,b=[b^1,b^2]^Tx=[x1,x2]T,b=[b1,b2]T,那么原方程可以表示为
A1x1+A2x2=b1,A2x1+A1x2=b2 A^1x^1+A^2x^2=b^1,A^2x^1+A^1x^2=b^2 A1x1+A2x2=b1,A2x1+A1x2=b2
两式相加/做差得
(A1+A2)(x1+x2)=b1+b2,(A1−A2)(x1−x2)=(b1−b2) (A^1+A^2)(x^1+x^2)=b^1+b^2,(A^1-A^2)(x^1-x^2)=(b^1-b^2) (A1+A2)(x1+x2)=b1+b2,(A1A2)(x1x2)=(b1b2)
ai1=ai+ai+n2,xi1=xi+xi+n2,bi1=bi+bi+n2,ai2=ai−ai+n2,xi2=xi−xi+n2,bi2=bi−bi−n2a^1_{i}=a_i+a_{i+\frac{n}{2}},x^1_i=x_i+x_{i+\frac{n}{2}},b^1_i=b_i+b_{i+\frac{n}{2}},a^2_{i}=a_i-a_{i+\frac{n}{2}},x^2_i=x_i-x_{i+\frac{n}{2}},b^2_i=b_i-b_{i-\frac{n}{2}}ai1=ai+ai+2n,xi1=xi+xi+2n,bi1=bi+bi+2n,ai2=aiai+2n,xi2=xixi+2n,bi2=bibi2n

那么简单分析可知a1a^1a1序列可以用同样的方式生成矩阵B1=A1+A2B^1=A^1+A^2B1=A1+A2a2a^2a2也可以生成B2=A1−A2B^2=A^1-A^2B2=A1A2,故求解原来的n×nn\times nn×n规模的矩阵方程问题转化为两个n2×n2\frac{n}{2}\times \frac{n}{2}2n×2n规模的矩阵方程问题
B1x1=b1,B2x2=b2 B^1x^1=b^1,B^2x^2=b^2 B1x1=b1,B2x2=b2
递归下去,以一般方程作为递归终点即可,时间复杂度O(nlogn)O(nlogn)O(nlogn),注意到这种解法其实就是模拟了FWTFWTFWT的过程

Code2

#include<cstdio>
using namespace std;
typedef long long ll;
#define maxn 262144+5
#define mod 1000000007
#define inv2 500000004
int n,a[19][maxn],b[19][maxn],x[19][maxn],y[19][maxn];
int mul(int x,int y)
{
	ll z=1ll*x*y;
	return z-z/mod*mod;
}
int add(int x,int y)
{
	x+=y;
	if(x>=mod)x-=mod;
	return x;
}
int Pow(int a,int b)
{
	int ans=1;
	while(b)
	{
		if(b&1)ans=mul(ans,a);
		a=mul(a,a);
		b>>=1;
	}
	return ans;
}
void Solve(int n,int m)
{
	if(m==0)
	{
		x[0][0]=mul(b[0][0],Pow(a[0][0],mod-2));
		return ;
	}
	for(int i=0;i<n/2;i++)
	{
		a[m-1][i]=add(a[m][i],a[m][i+n/2]);
		b[m-1][i]=add(b[m][i],b[m][i+n/2]);
	}
	Solve(n/2,m-1);
	for(int i=0;i<n/2;i++)y[m][i]=x[m-1][i];//x[i]+x[i+n/2]
	for(int i=0;i<n/2;i++)
	{
		a[m-1][i]=add(a[m][i],mod-a[m][i+n/2]);
		b[m-1][i]=add(b[m][i],mod-b[m][i+n/2]);
	}
	Solve(n/2,m-1);
	for(int i=0;i<n/2;i++)y[m][i+n/2]=x[m-1][i];//x[i]-x[i+n/2]
	for(int i=0;i<n/2;i++)
	{
		x[m][i]=mul(inv2,add(y[m][i],y[m][i+n/2]));
		x[m][i+n/2]=mul(inv2,add(y[m][i],mod-y[m][i+n/2]));
	}
}
int main()
{
	scanf("%d",&n);
	int m=0;
	while((1<<m)<n)m++;
	for(int i=0;i<n;i++)scanf("%d",&a[m][i]);
	for(int i=0;i<n;i++)scanf("%d",&b[m][i]);
	Solve(n,m);
	for(int i=0;i<n;i++)printf("%d\n",x[m][i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值