AGC051C - Flipper
一个无限大的网格,有些点染成了黑色。每次可以把一个2*3的矩形反色。
可以操作任意多次,求最后保留的黑点最少是多少。
\(n\le 10^5\)
考虑什么样的状态可以到达。
不变量:
- 每列的异或和,记为\(p_i\)。
- 每行中,列模\(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;
}