题目大意
有一个n*m的网格图,需要在每个格子内部填入A~C,要求满足:
① 每个2*2的小方格都要有ABC
② 边相邻格子内字母不同
给出初始若干格子相同(满足角相邻)的限制,判断是否存在合法解
2<=n,m<=2e3
题解
神必题……
首先假设给2*2的格子内角相邻的相同格子连斜边,则会连出一个图,每个2*2格子内都要有一条连边
然后在这个图里连通的都是同一个字母,所以会有一些连边情况不合法,比如
这样,则实际上外面四个圆圈都相等,所以会连一下,与右上的边冲突
显然所有连边同向合法,然后根据观察可得,将一个合法图的斜边进行任意行/列翻转仍然合法
所以可以根据行/列是否翻转设01状态,然后用给出的限制来对行/列状态连边(相同/相异),最后dfs判断是否有矛盾即可
证明:
可以设ABC对应012,然后对边相邻的格子连横向/纵向的边,边权为(上-下)%3 or (左-右)%3
然后观察发现一个2*2格子内,上下和左右的边权分别相等(确定一行/列的状态,只能是1/2),且当正对角线相同时上下=左右,反对角线相同时上下≠左右
把差值1/2变成0/1,设为行/列的权值;两种边决定行列权值相同/相异,则和上面差不多了
code
#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define ll long long
#define file
using namespace std;
int T,n,m,K,i,j,k,l,x,y;
int X1,Y1,X2,Y2;
int a[80001][3],ls[40001],len;
int f[40001];
bool ans;
void New(int x,int y,int z)
{
++len;
a[len][0]=y;
a[len][1]=ls[x];
ls[x]=len;
a[len][2]=z;
}
void dfs(int t)
{
int i;
for (i=ls[t]; i; i=a[i][1])
if (f[a[i][0]]==-1)
{
f[a[i][0]]=f[t]^a[i][2];
dfs(a[i][0]);
}
else
if (f[a[i][0]]!=(f[t]^a[i][2]))
ans=0;
}
int main()
{
scanf("%d",&T);
for (;T;--T)
{
len=0;
scanf("%d%d%d",&n,&m,&K);
fo(i,1,n+m) f[i]=-1,ls[i]=0;
fo(i,1,K)
{
scanf("%d%d%d%d",&X1,&Y1,&X2,&Y2);
x=min(X1,X2);
y=min(Y1,Y2);
if (x==X1 && y==Y1 || x==X2 && y==Y2) //1
{
New(x,n+y,0);
New(n+y,x,0);
}
else //2
{
New(x,n+y,1);
New(n+y,x,1);
}
}
ans=1;
fo(i,1,n+m)
if (f[i]==-1)
f[i]=0,dfs(i);
if (ans)
cout<<"YES"<<endl;
else
cout<<"NO"<<endl;
}
}