do_while_true

一言(ヒトコト)

「题解」ARC151D Binary Representations and Queries

克罗内克积(Kronecker Product)

大小为 \(m\times n\) 的矩阵 \(A\) 和大小为 \(p\times q\) 的矩阵 \(B\) 的克罗内克积 \(A\otimes B\) 被定义为:

\[\begin{pmatrix} a_{1,1}B & \cdots & a_{1,n}B\\ \vdots & \ddots & \vdots \\ a_{m,1}B & \cdots & a_{m,n}B \end{pmatrix} \]

显然不满足交换律,但是容易验证其满足结合律。

性质 1:\((A\otimes B)(C\otimes D)=(AC)\otimes (BD)\),其中 \(AC\) 表示矩阵 \(A\)\(C\) 的乘积。证明直接按定义写出矩阵来即可。

推论 1:\((A_1\otimes B_1\otimes \cdots \otimes Z_1)(A_2\otimes B_2\otimes \cdots \otimes Z_2)\cdots (A_n\otimes B_n\otimes \cdots \otimes Z_n)=(A_1A_2\cdots A_n)\otimes (B_1B_2\cdots B_n)\cdots \otimes (Z_1Z_2\cdots Z_n)\)

根据性质 1,不难通过每次取出每个括号的最前面的矩阵来证明。

推论 2:

假设 \(A,B,C,I_n\) 是大小为 \(n\times n\) 的矩阵,其中 \(I_n\) 为单位矩阵。那么即有:\(A \otimes B \otimes C = (A\otimes I_n\otimes I_n)(I_n\otimes B\otimes I_n)(I_n\otimes I_n\otimes C)= (A \otimes I_n \otimes I_n)(I_n \otimes B \otimes I_n)(I_n \otimes I_n \otimes C)\)

同样可以扩展到 \(k\) 个矩阵的情况。

再根据推论 1 将最后一个式子拆开,然后再以任意的方式合并,可以得出相邻的两个括号中间的矩阵 \(A\) 在夹在 \(I_n\) 中不同的位置时可以任意交换,而夹在相同的位置时则不行。

也就是 \((A \otimes I_n \otimes I_n)(I_n \otimes B \otimes I_n)=(I_n \otimes B \otimes I_n)(A \otimes I_n \otimes I_n)\) 成立,但不会有 \((A \otimes I_n \otimes I_n)(B \otimes I_n \otimes I_n)=(B \otimes I_n \otimes I_n)(A \otimes I_n \otimes I_n)\)

(不难理解,\(n=1\) 相当于矩阵乘法具有交换律了)

回到 ARC151D,对于仅有一个长度为 2 的向量 $\mathbf{a} $ 的特殊情况,构造矩阵 \(A_0,A_1\) 使得操作 \(Y=0\) 时等价于 \(\mathbf{a}\gets A_0\mathbf{a}\),操作 \(Y=1\) 时等价于 \(\mathbf{a}\gets A_1\mathbf{a}\)

仿照 FWT 的方法,对于长度为 \(2^n\) 的向量 \(\mathbf{a}\) 如果只想对第 \(X\) 位进行操作,那么相当于 \(\mathbf{a}\gets (I_2\otimes \cdots \otimes I_2\otimes A_Y\otimes I_2\otimes \cdots \otimes I_2)\mathbf{a}\),这里一共有 \(n\) 个大小为 \(2\times 2\) 的矩阵,\(A_Y\) 在第 \(X\) 个(最开头为第 0 个)。

根据结论,可以将 \(A_Y\) 处在不同位置的矩阵进行交换,但是 \(A_Y\) 相同的则不能。先将 \(X\) 相同的矩阵移动到一起,再根据推论 1 可以将同一 \(X\) 的矩阵乘在一起看作一个矩阵 \(B_X\)

那么现在问题就变成了计算 \((B_0\otimes I_n\otimes \cdots)(I_n\otimes B_1\otimes I_n\otimes \cdots)\cdots (I_n\otimes \cdots \otimes I_n\otimes B_n)\mathrm{a}\)

考虑左乘上一个 \((I_n\otimes \cdots \otimes B_i\otimes \cdots \otimes I_n)\) 具体是在固定除了第 \(i\) 位以外的所有位置,然后单独对 \(i\) 这一位左乘上 \(B_i\)(人话:对于每个第 \(X\)\(0\) 的数 \(v\),将 \(a_v,a_{v+2^{X}}\) 构成的向量左乘 \(B_i\)

直接按照这个写,就是和平常写高维前缀和 / 高维后缀和的 FMT 写法一样。

或者按照 FWT_xor 的写法也可以,就是把第 \(i\) 层合并的时候进行的线性变换改成左乘上 \(B_i\) 这个样子。

#include<cstdio>
#include<vector>
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<ctime>
#include<random>
#include<assert.h>
#define pb emplace_back
#define mp make_pair
#define fi first
#define se second
#define dbg(x) cerr<<"In Line "<< __LINE__<<" the "<<#x<<" = "<<x<<'\n';
#define dpi(x,y) cerr<<"In Line "<<__LINE__<<" the "<<#x<<" = "<<x<<" ; "<<"the "<<#y<<" = "<<y<<'\n';
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int>pii;
typedef pair<ll,int>pli;
typedef pair<ll,ll>pll;
typedef pair<int,ll>pil;
typedef vector<int>vi;
typedef vector<ll>vll;
typedef vector<pii>vpii;
typedef vector<pil>vpil;
template<typename T>T cmax(T &x, T y){return x=x>y?x:y;}
template<typename T>T cmin(T &x, T y){return x=x<y?x:y;}
template<typename T>
T &read(T &r){
	r=0;bool w=0;char ch=getchar();
	while(ch<'0'||ch>'9')w=ch=='-'?1:0,ch=getchar();
	while(ch>='0'&&ch<='9')r=r*10+(ch^48),ch=getchar();
	return r=w?-r:r;
}
template<typename T1,typename... T2>
void read(T1 &x,T2& ...y){read(x);read(y...);}
const int mod=998244353;
inline void cadd(int &x,int y){x=(x+y>=mod)?(x+y-mod):(x+y);}
inline void cdel(int &x,int y){x=(x-y<0)?(x-y+mod):(x-y);}
inline int add(int x,int y){return (x+y>=mod)?(x+y-mod):(x+y);}
inline int del(int x,int y){return (x-y<0)?(x-y+mod):(x-y);}
int qpow(int x,int y){
	int s=1;
	while(y){
		if(y&1)s=1ll*s*x%mod;
		x=1ll*x*x%mod;
		y>>=1;
	}
	return s;
}
const int N=18;
int n,q;
int a[(1<<N)+10];
int f[N][2][2];
int g[2][2][2],h[2][2];
void FWT(){
	for(int o=2,k=1,c=0;c<n;o<<=1,k<<=1,++c){
		for(int i=0;i<(1<<n);i+=o){
			for(int j=0;j<k;j++){
				int x=a[i+j],y=a[i+j+k];
				int u=add(1ll*x*f[c][0][0]%mod,1ll*y*f[c][0][1]%mod);
				int v=add(1ll*x*f[c][1][0]%mod,1ll*y*f[c][1][1]%mod);
				a[i+j]=u;a[i+j+k]=v;
			}
		}
	}
}
signed main(){
	#ifdef do_while_true
//		assert(freopen("data.in","r",stdin));
//		assert(freopen("data.out","w",stdout));
	#endif
	read(n);read(q);
	for(int i=0;i<(1<<n);i++)read(a[i]);
	for(int i=0;i<n;i++)f[i][0][0]=f[i][1][1]=1;
	g[0][0][0]=g[0][1][0]=g[0][1][1]=1;
	g[1][0][0]=g[1][0][1]=g[1][1][1]=1;
	while(q--){
		int x,y;read(x,y);
		for(int i=0;i<2;i++)
			for(int j=0;j<2;j++)
				for(int k=0;k<2;k++)
					cadd(h[i][j],1ll*g[y][i][k]*f[x][k][j]%mod);
		for(int i=0;i<2;i++)
			for(int j=0;j<2;j++)
				f[x][i][j]=h[i][j],h[i][j]=0;
	}
	FWT();
	for(int i=0;i<(1<<n);i++)cout << a[i] << ' ';
    #ifdef do_while_true
		cerr<<'\n'<<"Time:"<<1.0*clock()/CLOCKS_PER_SEC*1000<<" ms"<<'\n';
	#endif
	return 0;
}
posted @ 2023-02-07 15:06  do_while_true  阅读(27)  评论(0编辑  收藏  举报