E - Moat(状压&DSU)
考虑每个位置是在区域内还是区域外,每个合法答案与区域的分布情况一一对应。
先状压,然后所有包含给定输入的情况,先将 4 × 4 4\times 4 4×4的矩阵外围一圈变成 6 × 6 6\times 6 6×6的大小,然后用dsu进行判断,若区域外集合大小和区域内集合大小刚好是36则满足条件。 这里用 6 × 6 6\times 6 6×6的目的,方便以 ( 0 , 0 ) (0,0) (0,0)作为区域外的一个点,然后我们对区域内的一个点做标记,然后进行并查集即可。
时间复杂度: O ( 36 × 2 16 ) O(36\times 2^{16}) O(36×216)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int N=1e3+5,M=2e4+5,inf=0x3f3f3f3f,mod=1e9+7;
#define mst(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define PLL pair<ll,ll>
#define x first
#define y second
#define pb emplace_back
#define SZ(a) (int)a.size()
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define per(i,a,b) for(int i=a;i>=b;--i)
#define IOS ios::sync_with_stdio(false),cin.tie(nullptr)
void Print(int *a,int n){
for(int i=1;i<n;i++)
printf("%d ",a[i]);
printf("%d\n",a[n]);
}
int s[N],sz[N];
int find(int x){return x==s[x]?x:s[x]=find(s[x]);}
void Init(int n){
for(int i=0;i<n;i++) s[i]=i,sz[i]=1;
}
void merge(int u,int v){
u=find(u),v=find(v);
if(u!=v){
s[u]=v;
sz[v]+=sz[u];
}
}
int id(int i,int j){
return i*6+j;
}
int n;
bool vis[6][6];
int main(){
IOS;
rep(i,1,4)
rep(j,1,4){
int x;cin>>x;n=(n<<1)+x;
}
int st=1<<16;
int u=0,ans=0;
for(int k=0;k<st;k++)
if((k&n)==n)
{
Init(36);
int v=0;
for(int j=0;j<16;j++){
int x=j/4,y=j%4;
if(k>>j&1){
v=(x+1)*6+(y+1);
vis[x+1][y+1]=true;
}
else vis[x+1][y+1]=false;
}
for(int i=0;i<6;i++)
for(int j=0;j<6;j++){
if(i+1<6&&vis[i][j]==vis[i+1][j]) merge(id(i,j),id(i+1,j));
if(j+1<6&&vis[i][j]==vis[i][j+1]) merge(id(i,j),id(i,j+1));
}
ans+=(sz[find(0)]+sz[find(v)]==36);
}
cout<<ans<<'\n';
return 0;
}