并查集——vijos1944琵琶湖

来源:互联网 发布:网络攻防电子版 编辑:程序博客网 时间:2024/05/17 04:37

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]);}    






原创粉丝点击