題目鏈接:jzoj 3809
題目
爲了封印輝之環,古代塞姆利亞大陸的人民在異空間中建造了一座設備塔。
簡單的說,這座設備塔是一個漂浮在異空間中的圓柱體,圓柱體兩頭的圓是計算核心,而側面則是
傳輸信息所用的數據通道,劃分成 個區塊。
然而,隨着工作的繼續進行,他們希望把側面的一部分區塊也改造成其他模塊。然而,任何時候都
必須保證存在一條數據通道,能從圓柱體的一端通向另一端。
由於無法使用輝之環掌控下的計算系統,他們尋求你的幫助來解決這個問題。他們將逐個輸入想要
改造的區域,而你則執行所有可行的改造並忽略可能導致數據中斷的改造。
輸入
第一行,包含兩個整數;;,表示側面的長和寬,以及操作數。
接下來 行,每行包含三個整數; ,表示操作的區塊的座標。
數據保證不會對已經操作成功的區塊進行操作。
輸出
輸出一行,表示有多少個操作可以被執行。
樣例輸入
3 4 9
2 2
3 2
2 3
3 4
3 1
1 3
2 1
1 1
1 4
樣例輸出
6
數據範圍
對於分值爲的子任務,保證;;
對於分值爲的子任務,保證;;
對於分值爲的子任務,保證;;。
思路
這道題我們用並查集來做。
這種題一看到環,我們就會把它在複製一遍。
那麼我們可以看如果把已經可以執行的地方連城線,如果一個點和它複製一遍的對應點可以連起來的話,通道就沒了。
(可以自行想象了爲什麼,一個點的八個方向都可以連線)
然後對於怎麼看兩個點是否連通,我們可以枚舉這兩個點旁邊的點,看看它們旁邊的點能不能連通。因爲如果這兩個點連通,這兩個點有分別和我們要看的兩個點連通,那麼我們要看的兩個點也就肯定連通。
代碼
#include<cstdio>
using namespace std;
int n, m, k, x, y, a[3001][6001], fa[6000001], sum;
int dx[8] = {1, 1, 1, 0, 0, -1, -1, -1}, dy[8] = {1, 0, -1, 1, -1, 1, 0, -1};
int find(int x) {//並查集找祖先
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
void connect(int x, int y) {//並查集鏈接
int aa = find(x), bb = find(y);
if (aa < bb) fa[bb] = aa;
else if (aa > bb) fa[aa] = bb;
}
bool ch(int x, int &y) {//判斷是否能連並處理出界問題
if (x < 1 || x > n) return 0;
if (y < 1) y = 2 * m;
if (y > 2 * m) y = 1;
if (!a[x][y]) return 0;
return 1;
}
bool work(int x, int y) {
int xa = x, ya = y + m;//展開後的另一個點
for (int i = 0; i < 8; i++) {
int xx = x + dx[i], xy = y + dy[i];//第一個點找連接的
if (!ch(xx, xy)) continue;
for (int j = 0; j < 8; j++) {
int yx = xa + dx[j], yy = ya + dy[j];//第二個點找連接的
if (!ch(yx, yy)) continue;
if (find(a[xx][xy]) == find(a[yx][yy]) && a[xx][xy] && a[yx][yy]) return 0;//是否連通
}
}
return 1;
}
void findfather(int x, int y) {//找到附近的點
a[x][y] = ++sum;//記錄
fa[sum] = sum;//初始化
for (int i = 0; i < 8; i++) {
int ax = x + dx[i], ay = y + dy[i];
if (!ch(ax, ay)) continue;
connect(a[x][y], a[ax][ay]);//與附近的點連接
}
}
int main() {
scanf("%d %d %d", &n, &m, &k);//輸入
for (int hh = 1; hh <= k; hh++) {
scanf("%d %d", &x, &y);//輸入
if (work(x, y)) {//可以執行操作
findfather(x, y);//連點
findfather(x, y + m);//連點
}
}
printf("%d", sum / 2);//輸出可以的次數
return 0;
}