题解:P6405 [COCI2014-2015#2] ŠUMA

· · 题解

P6405 [COCI2014-2015#2] ŠUMA 题解

tag:可撤销并查集

题目大意

### 解题思路 首先我们显然只需要关心相邻格子是否某一个时刻高度相同。 设两个相邻格子 $(x_1,y_1)$ 和 $(x_2,y_2)$。分以下两种情况讨论: 1. $h_{x_1,y_1}=h_{x_2,y_2}$,且 $v_{x_1,y_1}=v_{x_2,y_2}$。这种情况下,无论哪个时刻这两个格子总是在同一个连通块内。可以用普通并查集直接合并在一起。 2. $(x_1,y_1)$ 永远追不上 $(x_2,y_2)$ 或永远不能被 $(x_2,y_2)$ 追上。这种情况下,这对相邻格是废掉的,不用管它。 3. 其他情况下,记录追上的时间和追上的两个格子。 现在,我们已经有了一个关键事件列表,只需要排序后用可撤销并查集将所有的同一时间发生的“高度相同”的事件用可撤销并查集合并,合并时答案对并查集大小取 max 即可。 每一个时间点做完之后,再利用可撤销并查集的特性全部撤销即可。 代码中使用同一个并查集,用是否记录副本的方式同时实现可撤销并查集和并查集。 ### 代码实现 ```cpp #include<bits/stdc++.h> #define ll long long #define ull unsigned long long #define rep(i,l,r) for(register ll i=(l);i<=(r);++i) #define Rep(i,l,r) for(register ll i=(r);i>=(l);--i) #define all(x) x.begin(),x.end() #define rp(i,j,a,b) rep(i,1,a)rep(j,1,n) using namespace std; const ll N=709,M=2e6; ll n,h[N][N],v[N][N]; struct HstInfo{ ll*addr; ll cpy; } hstP[M],hstSZ[M]; ll nH,ans; struct Node{ ll p,sz; } tr[M]; ll root(ll x){ return tr[x].p==x?x:root(tr[x].p); } ll unite(ll u,ll v,bool isR){ u=root(u); v=root(v); if(u==v)return 0; if(tr[u].sz>tr[v].sz)swap(u,v); if(isR){ ++nH; hstP[nH]=(HstInfo){&tr[u].p,tr[u].p}; hstSZ[nH]=(HstInfo){&tr[v].sz,tr[v].sz}; } tr[u].p=v; tr[v].sz+=tr[u].sz; return tr[v].sz; } void roll(){ *hstP[nH].addr=hstP[nH].cpy; *hstSZ[nH].addr=hstSZ[nH].cpy; --nH; } void clr(){ while(nH)roll(); } #define id(i,j) (i)*n-n+(j) struct Event{ ll id1,id2; ll dh,dv; }; vector<Event> events; bool cmp(const Event&A,const Event&B){ return A.dh*B.dv<B.dh*A.dv; } ll dx[]={0,1,0,-1}; ll dy[]={1,0,-1,0}; int main(){ cin>>n; rp(x,y,n,n)cin>>h[x][y]; rp(x,y,n,n)cin>>v[x][y]; rp(x,y,n,n)tr[id(x,y)]=(Node){id(x,y),1}; rp(x,y,n,n)rep(k,0,1){ ll nx=x+dx[k], ny=y+dy[k]; if(nx>n||ny>n)continue; ll dh=h[nx][ny]-h[x][y]; ll dv=v[x][y]-v[nx][ny]; if(!dv&&!dh)ans=max(ans,unite(id(x,y),id(nx,ny),0)); else if(dv>0&&dh>=0)events.push_back((Event){id(x,y),id(nx,ny),dh,dv}); else if(dv<0&&dh<=0)events.push_back((Event){id(nx,ny),id(x,y),-dh,-dv}); } sort(all(events),cmp); ll T=events.size(); rep(i,0,T-1){ Event &e=events[i]; ans=max(ans,unite(e.id1,e.id2,1)); if(i+1<T&&cmp(e,events[i+1]))clr(); } cout<<ans<<endl; return 0; } ```