AGC051C - Flipper

一个无限大的网格,有些点染成了黑色。每次可以把一个2*3的矩形反色。

可以操作任意多次,求最后保留的黑点最少是多少。

\(n\le 10^5\)


考虑什么样的状态可以到达。

不变量:

  1. 每列的异或和,记为\(p_i\)
  2. 每行中,列模\(3\)分组的异或和记为\(q_{i,k},k=0,1,2\)\(q_i\)要么相等,要么全部相反。

接下来证明其充分性:

如果状态\(A\)能到\(B\),由于转移是双向的,所以如果\(A,B\)能转移到同一个状态,那么可以互相到达。

先强行把所有的\(x\ge 2,y\ge 3\)的黑点全部染白。现在平面分成了四个部分,对这四个部分分别分析,发现确实等价。

\(t_k=\sum_{i}p_{3i+k},s_k=\sum_iq_{i,k}\)。显然必须满足\(t_k\equiv s_k\pmod 2\)

你需要安排\(q_i\),得到\(s_k\)\(t_k\)是给定的。则贡献为\(\sum_{k}\max(s_k,t_k)\)(此时可以视作一个矩阵,有些列的异或和为\(1\),有些行的异或和为\(1\),并且两者个数之差为偶数。把这些行列交成的子矩阵拉出来,成为一个长方形,把长方形分割出一个正方形放对角线,剩下的部分全部放一排)。

问题相当于:你有一些向量\((0,0,0),(1,0,0),(0,1,0),(0,0,1)\),可以把一些向量取反。把这些向量求和得到\(s\),最大化\(\sum_k\max(s_k,t_k)\)

枚举每个向量取反个数的奇偶性。然后对于\(s_k-t_k\)最大的,尝试把偶数个向量\(k\)取反使其变小。


using namespace std;
#include <bits/stdc++.h>
#define N 100005
int n;
map<int,int> p,q;
int t[3];
int c[4];
int id(int x){
	if (!(x==0 || x==1 || x==2 || x==4))
		x^=7;
	if (x==0) return 3;
	if (x==1) return 0;
	if (x==2) return 1;
	if (x==4) return 2;
}
int s[3];
int main(){
	freopen("in.txt","r",stdin);
	scanf("%d",&n);
	for (int i=1;i<=n;++i){
		int x,y;
		scanf("%d%d",&x,&y);
		p[y]^=1;
		q[x]^=1<<y%3;
	}
	for (auto it=p.begin();it!=p.end();++it)
		t[it->first%3]+=it->second;
	for (auto it=q.begin();it!=q.end();++it)
		c[id(it->second)]++;
	int ans=INT_MAX;
	int r[4];
	for (r[0]=0;r[0]<=1 && r[0]<=c[0];++r[0])
		for (r[1]=0;r[1]<=1 && r[1]<=c[1];++r[1])
			for (r[2]=0;r[2]<=1 && r[2]<=c[2];++r[2])
				for (r[3]=0;r[3]<=1 && r[3]<=c[3];++r[3]){
					s[0]=(c[0]-r[0])+r[1]+r[2]+r[3];
					s[1]=r[0]+(c[1]-r[1])+r[2]+r[3];
					s[2]=r[0]+r[1]+(c[2]-r[2])+r[3];
					if (((s[0]^t[0])|(s[1]^t[1])|(s[2]^t[2]))&1)
						continue;
					int fir=0,sec=-1;
					for (int j=1;j<=2;++j)
						if (s[j]-t[j]>s[fir]-t[fir])
							sec=fir,fir=j;
						else if (sec==-1 && s[j]-t[j]>s[sec]-t[sec])
							sec=j;
					if (s[fir]-t[fir]>0 && s[sec]-t[sec]<0){
						int d=min(c[fir]-r[fir],min(s[fir]-t[fir],-(s[sec]-t[sec])))/2*2;
						s[fir]-=d;
						s[sec]+=d;
						s[3^fir^sec]+=d;
					}
					ans=min(ans,max(s[0],t[0])+max(s[1],t[1])+max(s[2],t[2]));
				}
	printf("%d\n",ans);
	return 0;
}
posted @ 2021-03-10 16:51  jz_597  阅读(158)  评论(0编辑  收藏  举报