2018.11.04 NOIP训练 小水塘(并查集)

传送门
这是复习普及组的时候做过的题了。
之前一直觉得很难码没有去做。
现在发现可以用并查集直接水过去。
其实就是把题目中说的连通的部分的面积用带权并查集维护一下就行了。
代码:

#include<bits/stdc++.h>
using namespace std;
inline int read(){
	int ans=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))ans=(ans<<3)+(ans<<1)+(ch^48),ch=getchar();
	return ans;
}
const int N=105;
const double Big=1.214601836602552,Small=0.785398163397;
int n,m,stat[N][N],fa[N*N*4+5],id[N<<1][N<<1];
double siz[N*N*4+N];
char s[N];
inline int find(int x){return x==fa[x]?fa[x]:fa[x]=find(fa[x]);}
inline int idx(int a,int b,int c){return ((a-1)*m+b)*4+c;}
inline void merge(int a1,int b1,int c1,int a2,int b2,int c2){
	if(!a1||!b1||!a2||!b2||a1>n||a2>n||b1>m||b2>m)return;
	int fx=find(idx(a1,b1,c1)),fy=find(idx(a2,b2,c2));
	if(fx^fy)fa[fx]=fy,siz[fy]+=siz[fx];
}
int main(){
	n=read(),m=read();
	for(int i=1;i<=n;++i){
		scanf("%s",s+1);
		for(int j=1;j<=m;++j){
			stat[i][j]=s[j]^48;
			for(int k=1;k<=4;++k)fa[idx(i,j,k)]=idx(i,j,k);
		}
	}
	for(int i=1;i<=n;++i){
		for(int j=1;j<=m;++j){
			if(!stat[i][j]){
				siz[idx(i,j,1)]=siz[idx(i,j,4)]=Small;
				siz[idx(i,j,2)]=siz[idx(i,j,3)]=Big;
				merge(i,j,2,i,j,3);
			}
			else{
				siz[idx(i,j,1)]=siz[idx(i,j,4)]=Big;
				siz[idx(i,j,2)]=siz[idx(i,j,3)]=Small;
				merge(i,j,1,i,j,4);
			}
			merge(i,j,1,i-1,j,3),merge(i,j,2,i-1,j,4);
			merge(i,j,1,i,j-1,2),merge(i,j,3,i,j-1,4);
			id[(i-1)<<1][(j-1)<<1]=idx(i,j,1);
			id[(i-1)<<1][j<<1]=idx(i,j,2);
			id[i<<1][(j-1)<<1]=idx(i,j,3);
			id[i<<1][j<<1]=idx(i,j,4);
			id[(i<<1)-1][(j<<1)-1]=idx(i,j,stat[i][j]+3); 
		}
	}
	for(int i=read(),x,y;i;--i)x=read(),y=read(),printf("%.4lf\n",(x+y)&1?0.000:siz[find(id[x][y])]);
	return 0;
}

转载于:https://www.cnblogs.com/ldxcaicai/p/10084764.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值