P3663 题解
前言
看过了题解,好像没有一篇纯粹的并查集解法。
本题写了一晚上,原因是对拍是造的数据一直出锅,话说这种题写 data 程序是真麻烦,简直就是什么时候
data 写完,什么时候宣告着你对拍结束。好像原本就是这样。
分析
我们了解一下问题:在一个二位矩阵中,规定哪些相邻的节点被阻断了“连通性”,我们需要寻找出每个连通块,并且将每个连通块中的每个点(牛)与除此以外其余连通块中的每个点一次相连,问总共能形成多少边。
我们的需求十分明显,即找出这些连通块,我们考虑每个连通块之间肯定不连通,即每个连通块直接肯定有相连的墙阻挡,再考虑并查集的作用,维护每个连通块中的每个点为一个父亲。我们进行合并操作,将每个点向他的上下左右四个方向连边,表示联通,而他不连通的条件就是他可能与上下左右点之间有墙,我们就不合并中间有墙的点。
如何判断中间有墙呢?我们观察到 n 的范围只有 100 ,所以我们把二维矩阵使用一维的数组表示,即第 i 行第 j 列的数的大小为 :
我们再来想一下最后答案究竟怎么算,我们先枚举每个牛,找到他们的父亲节点,即代表他在哪个连通块,最后就能遍历出每个连通块中的牛数,使该连通块中的每头牛向其他连通块中的每头牛都连一条边,
即:
还有一个小技巧,就是建边的时候只需要建右,下即可,因为我们是建的双向边,自己想一下就好了。
Code
#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
const int N = 201;
int ans,lyf;
int n, m, r;
int re() {
int x = 0, f = 1;
char ch = getchar();
while (ch < '0'||ch > '9') {
if(ch == '-') f = -1;
ch = getchar();
}
while (ch <= '9'&&ch >= '0') {
x = x * 10 + ch - '0';
ch = getchar();
}
return x * f;
}
int num;
int f[N * N];
int sum[N * N];
bool p[N * N];
bool vis[N][N];
queue <int> q;
int find (int x ){
while (x != f[x])
x = f[x] = f[f[x]];
return x;
}
int wz(int x, int y) {
return (x - 1) * n + y;
}
int dx[4] = {0, 1};
int dy[4] = {1, 0};
int cnt;
int head[N * N];
struct bal {
int u, v;
int nxt;
};
bal eg[N * N];
void add(int u, int v) {
++cnt;
eg[cnt].nxt = head[u];
eg[cnt].u = u;
eg[cnt].v = v;
head[u] = cnt;
}
int main() {
// freopen("data.in", "r", stdin);
// freopen("baoli.out", "w", stdout);
int x1, x2, y1, y2;
n = re(); m = re(); r = re();
for(int i = 1; i <= r; i++) {
x1 = re(); y1 = re();
x2 = re(); y2 = re();
add(wz(x1, y1), wz(x2, y2));
add(wz(x2, y2), wz(x1, y1));
}
for(int i = 1; i <= n * n; i++)
f[i] = i;
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= n; j++) {
int a = wz(i, j);
for(int k = 0; k <= 1; k++) {
x1 = i + dx[k];
y1 = j + dy[k];
if(x1 > n || y1 > n)
continue;
int b = wz(x1, y1);
bool flag = 0;
for(int l = head[a]; l; l = eg[l].nxt) {
int v = eg[l].v;
if(v == b) {
flag = 1;
break;
}
}
if(flag) continue;
int aa = find(a), bb = find(b);
if(aa == bb) continue;
f[bb] = aa;
}
}
}
for(int i = 1; i <= m; i++) {
x1 = re(); y1 = re();
if(vis[x1][y1]) continue;
vis[x1][y1] = 1;
int pos = find(wz(x1, y1));
if(!p[pos])
p[pos] = 1,q.push(pos);
sum[pos] ++;
}
while (!q.empty()) {
int x = q.front(); q.pop();
ans += sum[x] * (m - sum[x]);
}
printf("%d\n", ans >> 1);
return 0;
}
/*
3 5 5
2 2 1 2
2 2 2 3
1 1 1 2
2 3 3 3
3 2 3 3
3 3
2 2
2 3
1 2
1 1
*/
扯淡
目前最优解是我!!!