CF1270I Xor on Figures 题解
前言 第一次做这种重定义矩阵+求逆题记录一下,感觉这个题好多人类智慧。 正文 0x00 题目分析 观察题目中的操作可得几个性质。 1. 首先一对 left(p,qright) 至多操作一次,否则操作次…
前言
第一次做这种重定义矩阵+求逆题记录一下,感觉这个题好多人类智慧。
正文
0x00 题目分析
观察题目中的操作可得几个性质。
-
首先一对 至多操作一次,否则操作次数将会变多。
-
操作具有交换律。
-
操作具有结合律(可以理解为在全 矩阵上操作,然后 xor 在一起,最终结果不变)。
-
操作具有自反性。
-
在上述条件下可以推出操作唯一性,即不存在最小化的说法,只需要找出合法操作即可。
0x01 抽象矩阵
为了标示和观察所有的 参数。可以将这些参数放入一个矩阵中:对于每一个 令 为 ;其余格子否则为 。
可以发现一对 可以抽象为一种矩阵乘法运算。假若有这样一种矩阵乘法可以使得,为 的格子乘 还是 并进行对应的异或;为 的格子乘上了 还是 这样异或操作则不会发生。
显然定义这样的操作为:
若
则
这样即可实现想要的操作。
由于操作自反性且目标为全 矩阵,现在我们的目标就是找到答案矩阵 使得:
也就是:
在重载的定一下找到一个 的逆矩阵即可。
0x03 矩阵求逆
令单位矩阵为 ,即任意矩阵 ,有 ,显然 其余位置是 。
猜想 。证明:
-
容易观察到 只有 可能有值,即只有 可能有值且为 。
-
只有 可能有值,应为 会被正反异或两次抵消。
-
由于原来 只有奇数个值,那么 也只能有奇数个值因为若有 时,一定会有两个值会被同时异或抵消,这样奇偶数性质不变。
综上可得:
那么
这样得到了一个很劣的时间复杂度 。但是至少能算了。
0x04 加速计算
使用类似于快速幂的方法二进制分解 ,这样只需要 次乘法,但是还是过不了。进而想到优化乘法。
大力观察题目发现 很小,也就是说 中的 很少,而且肯定越乘越少。这样当 没有值的时候就不枚举剩下两维了,这样单次乘法 。总时间复杂度达到了 。
0x05 代码实现
注意不要弄反乘法顺序了,要把 放在前面,不然时间复杂度无法保证。
AC CODE
#include<bits/stdc++.h>
// #define ONLINE_JUDGE
#define INPUT_DATA_TYPE long long
#define OUTPUT_DATA_TYPE int
INPUT_DATA_TYPE read(){register INPUT_DATA_TYPE x=0;register char f=0,c=getchar();while(c<'0'||'9'<c)f=(c=='-'),c=getchar();while('0'<=c&&c<='9')x=(x<<3)+(x<<1)+(c&15),c=getchar();return f?-x:x;}void print(OUTPUT_DATA_TYPE x){register char s[20];register int i=0;if(x<0){x=-x;putchar('-');}if(x==0){putchar('0');return;}while(x){s[i++]=x%10;x/=10;}while(i){putchar(s[--i]+'0');}return;}
int k,n,mask;
struct MAT{
long long mat[512][512];
void setOne(){
register int i,j;
for(i=0;i<n;++i)
for(j=0;j<n;++j) mat[i][j]=0;
mat[0][0]=1;
return;
}
MAT operator * (MAT another) const{
register int i,j,k,l;
register MAT ans;
ans.setOne();
ans.mat[0][0]=0;
for(i=0;i<n;++i)
for(j=0;j<n;++j)
if(mat[i][j])
for(k=0;k<n;++k)
for(l=0;l<n;++l)
ans.mat[(i+k)&mask][(j+l)&mask]^=mat[i][j]*another.mat[k][l];
return ans;
}
}A,F;
signed main(){
#ifndef ONLINE_JUDGE
freopen("name.in", "r", stdin);
freopen("name.out", "w", stdout);
#endif
register int i,j,x,y,res=0;
k=read();
n=1<<k;
mask=n-1;
for(i=0;i<n;++i)
for(j=0;j<n;++j)
A.mat[i][j]=read();
int q=read();
for(i=0;i<q;++i){
x=read()-1;
y=read()-1;
F.mat[x][y]^=1;
}
for(i=0;i<k;++i){
A=F*A;
F=F*F;
}
for(i=0;i<n;++i)
for(j=0;j<n;++j)
res+=(A.mat[i][j]!=0);
print(res);
#ifndef ONLINE_JUDGE
fclose(stdin);
fclose(stdout);
#endif
return 0;
}
总结
这种题要观察到其是矩阵异或乘法这样的性质,并分析这样的操作,善于观察才能完成。
谢谢你,读到这里。
本文同步自洛谷专栏,保留原始正文与发布时间。在洛谷查看原文 ↗