← 回到所有文章

CF1270I Xor on Figures 题解

前言 第一次做这种重定义矩阵+求逆题记录一下,感觉这个题好多人类智慧。 正文 0x00 题目分析 观察题目中的操作可得几个性质。 1. 首先一对 left(p,qright) 至多操作一次,否则操作次…

题解文章的简约插画封面

前言

第一次做这种重定义矩阵+求逆题记录一下,感觉这个题好多人类智慧。

正文

0x00 题目分析

观察题目中的操作可得几个性质。

  1. 首先一对 (p,q)\left(p,q\right) 至多操作一次,否则操作次数将会变多。

  2. 操作具有交换律。

  3. 操作具有结合律(可以理解为在全 00 矩阵上操作,然后 xor 在一起,最终结果不变)。

  4. 操作具有自反性。

  5. 在上述条件下可以推出操作唯一性,即不存在最小化的说法,只需要找出合法操作即可。

0x01 抽象矩阵

为了标示和观察所有的 x,yx,y 参数。可以将这些参数放入一个矩阵中:对于每一个 xi,yix_i,y_i 令 Fxi,yiF_{x_i,y_i} 为 11;其余格子否则为 00。

可以发现一对 (p,q,w)\left(p,q,w\right) 可以抽象为一种矩阵乘法运算。假若有这样一种矩阵乘法可以使得,为 11 的格子乘 ww 还是 ww 并进行对应的异或;为 00 的格子乘上了 ww 还是 00 这样异或操作则不会发生。

显然定义这样的操作为:

若

A×B=CA\times B=C

则

Ci,j=⨁x=02k⨁y=02kAx,y×B(x−i) mod 2k,(y−i) mod 2kC_{i,j}=\bigoplus_{x=0}^{2^k}\bigoplus_{y=0}^{2^k} A_{x,y}\times B_{(x-i)\bmod 2^k,(y-i)\bmod 2^k}

这样即可实现想要的操作。

由于操作自反性且目标为全 00 矩阵,现在我们的目标就是找到答案矩阵 AnsAns 使得:

A=F×AnsA=F\times Ans

也就是:

Ans=A×F−1Ans=A\times F^{-1}

在重载的定一下找到一个 FF 的逆矩阵即可。

0x03 矩阵求逆

令单位矩阵为 II,即任意矩阵 MM,有 I×M=MI\times M=M,显然 I0,0=1I_{0,0}=1 其余位置是 00。

猜想 F2k=IF^{2^k}=I。证明:

  1. 容易观察到 F2kF^{2^k} 只有 F2k×i mod 2k,2k×j mod 2kF_{2^k\times i\bmod 2^k,2^k\times j\bmod 2^k} 可能有值,即只有 F0,0F_{0,0} 可能有值且为 11。

  2. F2F^2 只有 F2×xi mod ,2×yi mod F_{2\times x_i\bmod,2\times y_i\bmod} 可能有值,应为 Fxi+xj,yi+yjF_{x_i+x_j,y_i+y_j} 会被正反异或两次抵消。

  3. 由于原来 FF 只有奇数个值,那么 F2F^2 也只能有奇数个值因为若有 2×xi≡2×xj2\times x_i\equiv 2\times x_j 时,一定会有两个值会被同时异或抵消,这样奇偶数性质不变。

综上可得:

F2k=IF^{2^k}=I

那么

F2k−1=F−1F^{2^k-1}=F^{-1}

这样得到了一个很劣的时间复杂度 O(25k)\mathcal{O}\left(2^{5k}\right)。但是至少能算了。

0x04 加速计算

使用类似于快速幂的方法二进制分解 2k−12^k-1,这样只需要 O(k)\mathcal{O}\left(k\right) 次乘法,但是还是过不了。进而想到优化乘法。

大力观察题目发现 tt 很小,也就是说 FF 中的 11 很少,而且肯定越乘越少。这样当 Fi,jF_{i,j} 没有值的时候就不枚举剩下两维了,这样单次乘法 O(22k)\mathcal{O}\left(2^{2k}\right)。总时间复杂度达到了 O(kt4k)\mathcal{O}\left(kt4^k\right)。

0x05 代码实现

注意不要弄反乘法顺序了,要把 FF 放在前面,不然时间复杂度无法保证。

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;
}

总结

这种题要观察到其是矩阵异或乘法这样的性质,并分析这样的操作,善于观察才能完成。

谢谢你,读到这里。

本文同步自洛谷专栏,保留原始正文与发布时间。在洛谷查看原文 ↗

搜索文章

输入关键词,发现一些值得阅读的文字。

探索文字里的小小世界esc 关闭