Codeforces 1270I - Xor on Figures(异或卷积)

Codeforces 题目传送门 & 洛谷题目传送门

一道挺不错的观察性质题,虽然只有一个比较重要的 observation,但现场看出来的难度还是相当大的,这也就导致现场只有 3 个人通过。

首先转化题意:对于两个二维形式幂级数 \(F,G\),我们定义其乘法 \([x^ny^m](F\times G)=\operatorname{xor}\limits_{p+r\equiv n\pmod{2^k},q+s\equiv m\pmod{2^k}}[x^py^q]F·[x^ry^s]G\)。说白了就是二维版本的循环卷积,那么我们如果设 \(P_{i,j}\) 表示我们在 \((i,j)\) 位置上执行了操作 \((i,j,P_{i,j})\),那么问题可转化为,求一个幂级数 \(P\),使得 \(P\times C=A\),其中 \(C_{x_i,y_i}=1\),其他位置上的数为 \(0\)\(A\) 为初始矩阵,最小化 \(P\) 中非零元素的个数。

如果 \(C\) 存在逆元(定义单位元 \(I\) 满足 \(I_{0,0}=1\),其他元素为 \(0\),这样显然有 \(I\times A=A\)),那么显然 \(P\) 只可能等于 \(A\times C^{-1}\)。接下来我们证明 \(C\) 存在逆元,且逆元为 \(C^{2^k-1}\),这也就是本题的 key observation。

我们考察 \(C^2\),打个表可以发现 \(C^2\) 的非零元素的 \(x,y\) 次数都是偶数。因为对于 \(C\) 中两个不同的非零位置 \(C[x_i][y_i],C[x_j][y_j]\),其对 \(C^2[x_i+x_j][y_i+y_j]\) 的贡献会被算两次,而由于我们的运算为异或,因此两个 \(1\) 异或起来相当于啥也没干,故最终非零位置上的值必然是形如 \(C[x_i][y_i]\times C[x_i][y_i]\) 贡献得到的,这样贡献到的位置的 \(x,y\) 次数必然都是偶数。

同理,\(C^4\) 的非零元素的 \(x,y\) 次数都是 \(4\) 的倍数,\(C^8\) 的非零元素的 \(x,y\) 次数都是 \(8\) 的倍数。如此归纳下去可以得到,\(C^{2^k}\) 非零元素的 \(x,y\) 次数都是 \(2^k\) 的倍数,而由于次数最高只能达到 \(2^k-1\),因此 \(C^{2^k}\) 的非零元素只可能是 \(x^0y^0\) 前的系数,又因为 \(t\) 是奇数,两个由 \(0/1\) 组成、非零元素个数都是奇数的幂级数的乘积也由 \(0/1\) 组成,且非零元素个数也是奇数,因此 \(C^{2^k}\) 中非零元素个数也是奇数,故 \([x^0y^0]C^{2^k}=1\),即 \(C^{2^k}=I\),证毕。

接下来考虑怎样求解答案,答案就是 \(A\times C^{2^k-1}\),但是两个幂级数直接相乘复杂度是 \(16^k\) 的,因此如果我们直接套个快速幂复杂度将会高达 \(k16^k\),一脸过不去。不过注意到根据上面的推论,\(C^{2^i}\) 非零元素个数与 \(t\) 同阶,因此我们可以将式子写成 \(A\times C^{2^{k-1}}\times C^{2^{k-2}}\times\cdots\times C^{2}\times C\),这样每次乘法可以做到 \(4^k·t\),总复杂度就将为 \(kt4^k\)

const int MAXK=512;
int k,t;
struct mat{
	ll a[MAXK+5][MAXK+5];
	mat(){memset(a,0,sizeof(a));}
	int getcnt(){
		int sum=0;
		for(int i=0;i<(1<<k);i++) for(int j=0;j<(1<<k);j++)
			sum+=(a[i][j]>0);
		return sum;
	}
	friend mat operator *(const mat &a,const mat &b){
		mat res;
		for(int i=0;i<(1<<k);i++) for(int j=0;j<(1<<k);j++) if(b.a[i][j])
			for(int x=0;x<(1<<k);x++) for(int y=0;y<(1<<k);y++)
				res.a[(i+x)&((1<<k)-1)][(j+y)&((1<<k)-1)]^=a.a[x][y]*b.a[i][j];
		return res;
	}
} A,T;
int main(){
	read(k);
	for(int i=0;i<(1<<k);i++) for(int j=0;j<(1<<k);j++)
		read(A.a[i][j]);
	read(t);
	for(int _=1,x,y;_<=t;_++) read(x),read(y),T.a[x-1][y-1]=1;
	for(int _=0;_<k;_++,T=T*T) A=A*T,assert(T.getcnt()<=t);
	printf("%d\n",A.getcnt());
	return 0;
}
posted @ 2022-03-11 10:57  tzc_wk  阅读(139)  评论(0)    收藏  举报