CodeForces 1270I Xor on Figures
https://codeforces.com/contest/1270/problem/I
有一个 (2^k imes 2^k(1 le k le 9)) 的网格,其中 ((i,j)) 位置上的数字为 (a_{i,j}(0 le a_{i,j} < 2^{60})) .
这个网格是循环的,即 ((i,2^k)) 的右边是 ((i,1)) , ((2^k,i)) 的下面是 ((1,i))
有一个网格图(不一定联通) (F) ,它包含 (t) 个两两不同的格子(((1 le t le min{99,4^k})) 且 (t) 为奇数) ((x_1,y_1),(x_2,y_2),cdots,(x_t,y_t)(1 le x_i,y_i le 2^k)) .
一次操作可以选择一个格子 ((x,y)(1 le x,y le 2^k)) 和一个数字 (p) ,然后令所有 ((x+x_i mod 2^k,y+y_i mod 2^k)(1le i le t)) 上的数字异或 (p) .
问至少需要多少次操作令所有格子中的数 (=0) .若不存在,输出 (-1)
Tutorial
https://www.luogu.com.cn/problem/solution/CF1270I
定义新矩阵乘法 (A imes B =C) ,其中
则我们需要的就是找到是否存在这样一个矩阵 (C) ,满足 (C imes F = A) ,且 (C) 中非零数的位置最少.
假如我们可以找到 (F) 在新矩阵乘法意义下面的逆元.那么可以证明 (C) 是存在且唯一的.
考虑 (F imes F) 的形状,其中有贡献的位置是 ((x_i+x_j mod 2^k,y_i+y_j mod 2^k)(1 le i,j le t)) 的形式.考虑若 (i ot= j) 那么 ((i,j),(j,i)) 分别贡献一次后这个位置上的数就会变为 (0) .
所以最后 (F imes F) 的所有非零位置就是 ((2x_i mod 2^k,2y_i mod 2^k)) .
那么 (F^{2^k}) 的所有非零位置就是 ((2^kx_i mod 2^k,2^ky_i mod 2^k)=(0,0)) ,而由于 (t) 是奇数,所以总是有奇数个 (1) ,所以 ((0,0)) 位置上最后为 (1) .而这相当于在新矩阵乘法意义下的单位矩阵 (I)
则
由于 (F) 中的非零位置只有 (t) 个,所以一次新矩阵乘法的复杂度是 (O(4^kt)) 的.
总时间复杂度 (O(kt4^k))
Code
#include <cassert>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <vector>
#define fi first
#define se second
using namespace std;
inline char nc()
{
static char buf[100000],*l=buf,*r=buf;
return l==r&&(r=(l=buf)+fread(buf,1,100000,stdin),l==r)?EOF:*l++;
}
template<class T> void read(T &x)
{
x=0; int f=1,ch=nc();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=nc();}
while(ch>='0'&&ch<='9'){x=x*10-'0'+ch;ch=nc();}
x*=f;
}
typedef long long ll;
typedef pair<int,int> pii;
const int maxk=9;
const int maxn=1<<maxk;
const int maxt=99;
int k;
int n;
int t;
ll a[2][maxn][maxn];
pii F[maxt];
inline int add(int x) {return x>=n?x-n:x;}
inline int sub(int x) {return x<0?x+n:x;}
int main()
{
// freopen("in.txt","r",stdin);
read(k),n=1<<k;
int cur=0;
for(int i=0;i<n;++i) for(int j=0;j<n;++j)
{
read(a[cur][i][j]);
}
read(t);
for(int i=0;i<t;++i)
{
read(F[i].fi),read(F[i].se);
}
for(int i=0;i<k;++i)
{
cur^=1;
memset(a[cur],0,sizeof(a[cur]));
for(int x=0;x<n;++x) for(int y=0;y<n;++y)
{
for(int j=0;j<t;++j)
{
a[cur][x][y]^=a[cur^1][sub(x-F[j].fi)][sub(y-F[j].se)];
}
}
for(int j=0;j<t;++j)
{
F[j].fi=add(F[j].fi<<1);
F[j].se=add(F[j].se<<1);
}
}
int an=0;
for(int x=0;x<n;++x) for(int y=0;y<n;++y)
{
if(a[cur][x][y]) ++an;
}
printf("%d
",an);
return 0;
}