克罗内克积(Kronecker Product)
大小为 的矩阵 和大小为 的矩阵 的克罗内克积 被定义为:
显然不满足交换律,但是容易验证其满足结合律。
性质 1:,其中 表示矩阵 与 的乘积。证明直接按定义写出矩阵来即可。
推论 1:
根据性质 1,不难通过每次取出每个括号的最前面的矩阵来证明。
推论 2:
假设 是大小为 的矩阵,其中 为单位矩阵。那么即有:.
同样可以扩展到 个矩阵的情况。
再根据推论 1 将最后一个式子拆开,然后再以任意的方式合并,可以得出相邻的两个括号中间的矩阵 在夹在 中不同的位置时可以任意交换,而夹在相同的位置时则不行。
也就是 成立,但不会有
(不难理解, 相当于矩阵乘法具有交换律了)
回到 ARC151D,对于仅有一个长度为 2 的向量 的特殊情况,构造矩阵 使得操作 时等价于 ,操作 时等价于 .
仿照 FWT 的方法,对于长度为 的向量 如果只想对第 位进行操作,那么相当于 ,这里一共有 个大小为 的矩阵, 在第 个(最开头为第 0 个)。
根据结论,可以将 处在不同位置的矩阵进行交换,但是 相同的则不能。先将 相同的矩阵移动到一起,再根据推论 1 可以将同一 的矩阵乘在一起看作一个矩阵 .
那么现在问题就变成了计算 .
考虑左乘上一个 具体是在固定除了第 位以外的所有位置,然后单独对 这一位左乘上 (人话:对于每个第 为 的数 ,将 构成的向量左乘 )
直接按照这个写,就是和平常写高维前缀和 / 高维后缀和的 FMT 写法一样。
或者按照 FWT_xor 的写法也可以,就是把第 层合并的时候进行的线性变换改成左乘上 这个样子。
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
2022-02-07 Miller–Rabin 素数测试
2022-02-07 杜教筛