peiwenjun's blog 没有知识的荒原

CF1270I Xor on Figures 题解

题目描述

给定一个 \(2^k\times 2^k\) 的矩阵 \(A\) ,下标从零开始。

给定长为 \(t\) 的数组 \(x_i,y_i\)

每次操作你可以任选 \(p,q,w\) ,然后对 \(\forall 1\le i\le t\) ,将 \(A[(x_i+p)\bmod 2^k][(y_i+q)\bmod 2^k]\) 异或上 \(w\)

求使得 \(A\) 变成全零矩阵的最小操作次数。

数据范围

  • \(1\le k\le 9,0\le a_{i,j}\lt 2^{60}\)
  • \(1\le t\le\min(99,4^k)\) ,保证 \(t\) 为奇数。
  • \(1\le x_i,y_i\le 2^k\)

时间限制 \(\texttt{5s}\) ,空间限制 \(\texttt{256MB}\)

分析

思维难度极高,看完题解感觉好简单。

定义二维数组的异或卷积:

\[(a\times b)[x][y]=\bigoplus_{i=0}^{2^k-1}\bigoplus_{j=0}^{2^k-1}a[i][j]\cdot b[x-i][y-j] \]

其中两维下标都是在 \(\bmod 2^k\) 意义下。

显然一组 \((p,q)\) 只会被操作至多一次。

记二维数组 \(F\) 满足 \(F[x_i][y_i]=1\) ,操作矩阵 \(G\) 满足 \(G[p][q]=w\)

根据题目条件有 \(F\times G=A\) ,目标 \(G\) 的非零元个数最小。

\(\texttt{Key observation}\)\(F^{2^k}=I\) ,其中 \(I[x][y]=[x=0\wedge y=0]\)

证明也很简单:

\(F\) 的高次幂值域为 \(\{0,1\}\)

考虑 \(F^2\) ,如果 \(i\neq j\) ,那么 \((i,j),(j,i)\) 都会贡献到 \(F[x_i+x_j][y_i+y_j]\) ,根据异或的性质等于没有贡献。

所以仅有 \(i=j\)\((i,j)\) 才会产生贡献,因此 \(F^2\) 中非零元横纵下标均为偶数。

依此类推, \(F^{2^k}\) 中仅有横纵坐标均为 \(2^k\) 倍数的元素(即 \(F[0][0]\) )可能非零。

再观察 \(1\) 的个数的奇偶性,由于 \(F\) 中有奇数个 \(1\) (因为 \(t\) 为奇数),所以 \(F^{2^k}\) 中也有奇数个 \(1\) ,即 \(F[0][0]=1\)

因此 \(F\) 逆元存在,并且 \(F^{-1}=F^{2^k-1}\)

因此 \(G\) 唯一确定,并且 \(G=A\times F^{2^k-1}\)

由于 \(F\) 中至多只有 \(t\)\(1\) ,所以单次卷积可以做到 \(\mathcal O(t4^k)\)

时间复杂度 \(\mathcal O(kt4^k)\)

#include<bits/stdc++.h>
#define vvi vector<vector<long long>>
using namespace std;
const int maxn=1e5+5;
int k,n,t,x,y,res;
vvi a,f;
vvi operator*(const vvi &a,const vvi &b)
{
    static vvi res(n);
    for(int i=0;i<n;i++) res[i].clear(),res[i].resize(n);
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
        {
            if(!b[i][j]) continue;
            for(int p=0;p<n;p++)
                for(int q=0;q<n;q++)
                    res[(i+p)%n][(j+q)%n]^=a[p][q];
        }
    return res;
}
int main()
{
    scanf("%d",&k),n=1<<k,a.resize(n),f.resize(n);
    for(int i=0;i<n;i++)
    {
        a[i].resize(n),f[i].resize(n);
        for(int j=0;j<n;j++) scanf("%lld",&a[i][j]);
    }
    scanf("%d",&t);
    while(t--) scanf("%d%d",&x,&y),f[x-1][y-1]=1;
    for(int i=0;i<k;i++) a=a*f,f=f*f;
    for(int i=0;i<n;i++)
        for(int j=0;j<n;j++)
            res+=a[i][j]!=0;
    printf("%d\n",res);
    return 0;
}

posted on 2022-08-18 21:32  peiwenjun  阅读(11)  评论(0)    收藏  举报

导航