这题正解脑洞有点大。。一开始并没有想到正解,只是会二分暴力= =
后来膜了一发题解发现原来还有人二分+可持久化并查集做的,不过这是针对t不同的情况下= =以后可以出个题祸害社会
正解是先把任意两点之间连边,记录一下出发点和到达点,然后按照边的大小排序,从小到大做,每一次用并查集将可以互通的两块联合在一起,直接计算贡献。。
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int inf=1e9;
int n,m;
const int dx[2]={0,1};
const int dy[2]={1,0};
const int N=600*600;
int ez[N],ey[N],ex[N];
int f[N],siz[N],cnt[N],eid[N];
int t,h[600][600],id[600][600],tot;
bool cmp(int a,int b)
{
return ez[a]<ez[b];
}
inline int find(int x)
{
return x==f[x]?x:f[x]=find(f[x]);
}
int main()
{
scanf("%d%d%d",&n,&m,&t);
fo(i,1,n)
{
fo(j,1,m)
{
scanf("%d",&h[i][j]);
id[i][j]=++tot;
f[tot]=tot;
siz[tot]=1;
}
}
tot=0;
fo(i,1,n)
{
fo(j,1,m)
{
int x;
scanf("%d",&x);
if (x)cnt[id[i][j]]=1;
fo(k,0,1)
{
int x=i+dx[k];
int y=j+dy[k];
if (x>=1&&x<=n&&y>=1&&y<=m)
{
ex[++tot]=id[i][j];
ey[tot]=id[x][y];
ez[tot]=abs(h[i][j]-h[x][y]);
eid[tot]=tot;
}
}
}
}
sort(eid+1,eid+1+tot,cmp);
ll ans=0;
fo(i,1,tot)
{
int x=eid[i];
int a=find(ex[x]),b=find(ey[x]);
if (a==b)continue;
if (siz[a]+siz[b]>=t)
{
if (siz[a]<t)ans+=1ll*cnt[a]*ez[x];
if (siz[b]<t)ans+=1ll*cnt[b]*ez[x];
//注意这里直接把ez[x]当作边是因为已经从小到大排序了,所以能保证ez[x]是两个块中最大的边。
}
if (siz[a]<siz[b])swap(a,b);
f[b]=a;
siz[a]+=siz[b];
cnt[a]+=cnt[b];
}
printf("%lld\n",ans);
}