题意:输入一个n * m 矩阵,每个格子里都有1个[1,1e9]的正整数。再输入T个整数ti(0<=t1<=t2<=...<=tT<=1e9),对于每个ti,输出大于ti的正整数组成多少个四连块。
思路:1.并查集计算有多少连通块
2.因为T个整数是按大小顺序已经排好的,所以可以倒着来在算完ti的基础上计算t(i - 1),每个格子记为一个点,将格子按照格子内的值从小到大排列,从最后一个格子开始判断是否大于ti,然后查看四周是否可以合并。当所有的格子都枚举完时,若ti还没计算完,这时所有没算完的ti都继承已经计算完成的最后一个ti的答案,因为此时大的格子的连通块数一定也是小格子的答案。
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
typedef long long ll;
using namespace std;
const int maxn = 1000 + 2;
int a[maxn][maxn];
int fa[maxn * maxn];
int dx[] = {0,1,0,-1};
int dy[] = {1,0,-1,0};
int n,m,T,ans[100000+ 2];
struct node
{
int x,y,val;
bool operator < (const node & q)const
{
return val < q.val;
}
} poi[maxn*maxn];
int found(int p)
{
if(fa[p] == p) return p;
return fa[p] = found(fa[p]);
}
int main()
{
int N; scanf("%d",&N);
while(N--)
{
scanf("%d%d",&n,&m);
for(int i = 0; i < n; i++)
{
for(int j = 0; j < m; j++)
{
scanf("%d",&a[i][j]);
poi[i * m + j].x = i;
poi[i * m + j].y = j;
poi[i * m + j].val = a[i][j];
}
}
sort(poi,poi + n * m);
memset(fa,-1,sizeof(fa));
int sum = 0; scanf("%d",&T);
for(int i = 0; i < T; i++) scanf("%d",&ans[i]);
int k = n * m - 1;
for(int i = T - 1; i >= 0; i--) //要求的
{
while(k >= 0 && ans[i] < poi[k].val)
{
int pos = poi[k].x * m + poi[k].y;
if(fa[pos] == -1) fa[pos] = pos,sum++;
for(int j = 0; j < 4; j++)
{
int nx = poi[k].x + dx[j];
int ny = poi[k].y + dy[j];
if(nx >= 0 && nx < n && ny >= 0 && ny < m && a[nx][ny] > ans[i])
{
int ppos = nx * m + ny;
if(fa[ppos] != -1)
{
int u = found(fa[pos]);
int v = found(fa[ppos]);
if(u != v) fa[v] = u,sum--;
}
}
}
k--;
}
if(k < 0)
{
for(; i >= 0; i--) ans[i] = sum;
break;
}
ans[i] = sum;
}
for(int i = 0; i < T; i++) printf("%d ",ans[i]);
printf("\n");
}
return 0;
}