洛谷 P6405 [COCI 2014/2015 #2] ŠUMA 题解
Solution
不难发现我们只需要关心相邻的格子在什么时候相等,仅在相等时连边,然后找出现过的最大连通块。
设两个相邻格子分别为 \((i_1,j_1)\) 和 \((i_2,j_2)\),相等时刻为 \(x\)。不难列出方程 \((v_{i_2,j_2}-v_{i_1,j_1})x=h_{i_1,j_1}-h_{i_2,j_2}\)。
设 \(A=h_{i_1,j_1}-h_{i_2,j_2}\),\(B=v_{i_2,j_2}-v_{i_1,j_1}\)。
- 若 \(A=0\),\(B=0\),则两个格子高度永远相等,连边是永久边。
- 若 \(A\neq0\),\(B=0\),无解。
- 若 \(B\neq 0\),\(x\) 有唯一解,因此边是瞬时边。注意 \(x\) 必须 \(\ge 0\)。
我们先处理出所有边,建一个并查集维护永久边形成的连通块并缩成点。再用第二个并查集维护这些新点之上的连通块。
然后将所有瞬时边按出现时刻排序后,每次将所有同一时刻出现的边加入第二个并查集并更新答案即可。但这样需要多次初始化第二个并查集,这可以用时间戳+懒标记 \(O(1)\) 实现。
浮点数可能会掉精度,最好手写分数存储时刻。
Code
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i(a);i<b;++i)
#define rept(i,a,b) for(int i(a);i<=b;++i)
#define int long long
#define il inline
using namespace std;
constexpr int N=701,S=N*N,dx[2]={0,1},dy[2]={1,0};
struct Frac{
int a,b;
Frac(int _a=0,int _b=1):a(_a),b(_b){
int g=__gcd(a,b);
a/=g,b/=g;
if(b<0) a=-a,b=-b;
}
il bool operator<(const Frac &rhs) const {
return a*rhs.b<rhs.a*b;
}
};
struct Edge{
int u,v;
Frac t;
il bool operator<(const Edge &rhs) const {
return t<rhs.t;
}
}e[S<<1];
int h[N][N],v[N][N];
int f1[S],s1[S]; // 第一个并查集
int f2[S],l2[S],s2[S],stamp; // 第二个并查集
// l2:f2上一次被访问的时间
// stamp:当前时间戳(懒标记)
int n,m,ans;
int find1(int x){
return x==f1[x]?x:f1[x]=find1(f1[x]);
}
int find2(int x){
if(l2[x]<stamp){
l2[x]=stamp;
f2[x]=x;
s2[x]=s1[x];
}
return x==f2[x]?x:f2[x]=find2(f2[x]);
}
il int calc(int i,int j){
return n*(i-1)+j;
}
void solve(int l,int r){
// e[l]~e[r]为出现在同一个时刻的边
++stamp;
rept(i,l,r){
int u=find2(find1(e[i].u)),v=find2(find1(e[i].v));
if(u^v){
f2[u]=v;
s2[v]+=s2[u];
ans=max(ans,s2[v]);
}
}
}
signed main(){
cin.tie(0)->sync_with_stdio(0);
cin>>n;
rept(i,1,n) rept(j,1,n) cin>>h[i][j];
rept(i,1,n) rept(j,1,n) cin>>v[i][j];
rept(i,1,n*n) f1[i]=i,s1[i]=1;
rept(i,1,n){
rept(j,1,n){
rep(d,0,2){
int di=i+dx[d];
int dj=j+dy[d];
int A=h[i][j]-h[di][dj];
int B=v[di][dj]-v[i][j];
if(!A&&!B){ // 永久边
int u=find1(calc(i,j)),v=find1(calc(di,dj));
if(u^v){
f1[u]=v;
s1[v]+=s1[u];
ans=max(ans,s1[v]);
}
}else if(B){ // 瞬时边
Frac t(A,B);
if(t.a>=0) e[++m]={calc(i,j),calc(di,dj),t};
}
}
}
}
sort(e+1,e+m+1);
int l=1,r=1;
for(int l=1,r=1;r<=m;){
if(r==m||e[r].t<e[r+1].t){
solve(l,r);
l=r+1;
}
++r;
}
cout<<ans;
return 0;
}

浙公网安备 33010602011771号