并查集——vijos1944琵琶湖


vijos1944

下午除了订正只做了这题啊。。

这题我感觉我的xjb算法已经有点用处了……虽然我不知道别人怎么搞的,但是我download觉得我的做法很玄


首先,并查集并不支持断开,所以我们不可能去顺序模拟题意

因此倒序

在Ti时会有哪些点浮出水面


那首先我们二分出点(x,y)在什么时刻浮出水面,然后把压缩后的点向这一个时刻连一条边……

是不是很奇怪……而且有可能会有自边是吧。不管他,反正当森林看


这个用倒挂才可以存下……我本来不知道怎么存的下,因为第一反应显然是邻接表啊。然后想,如果邻接表可以存的话那边表应该也可以存


于是边表


暴力的做法是直接出来一个点,上下左右跑四次并查集,然后n*m判一遍。……可能和直接BFS差不多?


然后一个点浮出水面后,首先对答案的贡献是1,然后去考虑他把上下左右连接起来对答案的贡献,一次有效对答案的贡献是-1,然后直接用并查集维护就可以了。


#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <queue>
#include <string>
#include <map>
#include <cstring>
#include <ctime>
#include <vector>
#define inf 1e9
#define ll long long
#define For(i,j,k) for(int i=j;i<=k;i++)
#define Dow(i,j,k) for(int i=k;i>=j;i--)
using namespace std;
int a[1001][1001],que[100001],n,m,T,fa[1000001],poi[1000001],f[1000001],nxt[1000001],cnt,ANS[100001],size[1000001],ans;
int dx[]={0,1,0,-1,0};
int dy[]={0,0,1,0,-1};
inline int c(int x,int y){return (x-1)*m+y;}
inline void add(int x,int y)
{
	poi[++cnt]=y;nxt[cnt]=f[x];f[x]=cnt;
}
inline int get(int x){return fa[x]==x?x:fa[x]=get(fa[x]);}
inline void merge(int x,int y)
{
	int tx=get(x),ty=get(y);
	fa[tx]=ty;
	if(tx!=ty)
		ans-=1;	
}
inline void doit(int x)
{
	for(int i=f[x];i;i=nxt[i])
	{
	
		int ty=(poi[i]-1)%m+1,tx=(poi[i]-1)/m+1;	
		fa[poi[i]]=poi[i];size[poi[i]]=1;
		ans++;
		For(dir,1,4)
		{
			int px=tx+dx[dir],py=ty+dy[dir];
			if(px>=1&&py>=1&&px<=n&&py<=m)	
				if(fa[c(px,py)])	
					merge(c(tx,ty),c(px,py));
		}
	}
	ANS[x]=ans;
}
inline void get(int x,int y)
{
	int l=1,r=T,t=0;
	while(l<=r)
	{
		int mid=(l+r)/2;
		if(que[mid]<a[x][y]) t=max(t,mid),l=mid+1;else r=mid-1;
	}
	add(t,c(x,y));
}
int main()
{
	scanf("%d%d",&n,&m);
	For(i,1,n)
		For(j,1,m)	scanf("%d",&a[i][j]);
	scanf("%d",&T);
	For(i,1,T)	scanf("%d",&que[i]);
	For(i,1,n)
		For(j,1,m)	
			get(i,j);
	Dow(i,1,T)	doit(i);
	For(i,1,T)	printf("%d ",ANS[i]);
}
    






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值