設備塔

設備塔

題目鏈接:jzoj 3809

題目

爲了封印輝之環,古代塞姆利亞大陸的人民在異空間中建造了一座設備塔。
簡單的說,這座設備塔是一個漂浮在異空間中的圓柱體,圓柱體兩頭的圓是計算核心,而側面則是
傳輸信息所用的數據通道,劃分成NmN*m 個區塊。
然而,隨着工作的繼續進行,他們希望把側面的一部分區塊也改造成其他模塊。然而,任何時候都
必須保證存在一條數據通道,能從圓柱體的一端通向另一端。
由於無法使用輝之環掌控下的計算系統,他們尋求你的幫助來解決這個問題。他們將逐個輸入想要
改造的區域,而你則執行所有可行的改造並忽略可能導致數據中斷的改造。

輸入

第一行,包含兩個整數NN;MM;KK,表示側面的長和寬,以及操作數。
接下來KK 行,每行包含三個整數xixi; yiyi,表示操作的區塊的座標。(0<=y=<M)(0<=y=<M)
數據保證不會對已經操作成功的區塊進行操作。

輸出

輸出一行,表示有多少個操作可以被執行。

樣例輸入

3 4 9
2 2
3 2
2 3
3 4
3 1
1 3
2 1
1 1
1 4

樣例輸出

6

數據範圍

對於分值爲3030的子任務11,保證NN;M<=100M <=100;K<=5000K<= 5000
對於分值爲3030的子任務22,保證NN;M<=3000M <=3000;K<=5000K <= 5000
對於分值爲4040的子任務33,保證NN;M<=3000M <=3000;K<=300000K <= 300000

思路

這道題我們用並查集來做。
這種題一看到環,我們就會把它在複製一遍。

那麼我們可以看如果把已經可以執行的地方連城線,如果一個點和它複製一遍的對應點可以連起來的話,通道就沒了。
(可以自行想象了爲什麼,一個點的八個方向都可以連線)

然後對於怎麼看兩個點是否連通,我們可以枚舉這兩個點旁邊的點,看看它們旁邊的點能不能連通。因爲如果這兩個點連通,這兩個點有分別和我們要看的兩個點連通,那麼我們要看的兩個點也就肯定連通。

代碼

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章