E - Moat(状压&DSU)

E - Moat(状压&DSU)

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;
}
上一篇:【题解】CF1284G Seollal


下一篇:线段树分治 ---- CF1217F - Forced Online Queries Problem(假离线 可撤销并查集 + 线段树分治)详解