【HYSBZOJ】【搜索】[Hnoi2019]校園旅行

HYSBZOJ 5492 [Hnoi2019]校園旅行

題目大意

◇題目傳送門◆

分析

首先我們不難發現,一個迴文串去掉首尾之後一定是一個迴文串。

所以我們可以根據這個性質來做:

假設(x,y)(x,y)是一對可以通過迴文串相互到達的點對,那麼我們可以枚舉xx的鄰接點uuyy的鄰接點vv,當u,vu,v同色時,它們就可以通過迴文串相互到達。然後我們按照 BFS 順序轉移即可。

這樣做我們需要枚舉所有的邊,複雜度爲O(M2)O(M^2)的,顯然過不了這題。

代碼就不給了吧。。。

考慮如何減少邊數。

考慮將連接相同顏色的邊拿出來,這樣原圖就被劃分成若干個連通塊。

這樣的話如果某個連通塊是二分圖,那麼我們從XX部走到YY部會經過恰好奇數條邊,而從XX部走回XX部或者從YY部走回YY部會經過恰好偶數條邊。也就是說,我們恰好會得到奇數/偶數個00或者11

實際上,我們對於這個連通塊,保留它的一棵生成樹就夠了。因爲題目中沒有要求只走簡單路徑,所以在原來的二分圖上走環就可以轉化成在生成樹上走重複的邊了。這樣只留下一棵生成樹的話對於答案是沒有影響的。

但當這個連通塊不是二分圖的時候,我們可以發現通過不斷地繞着環走可以改變長度奇偶性,所以我們在這個圖上的生成樹上隨便加一個自環就可以了。

對於連着不同顏色的邊也做同樣的處理,不難發現這時它一定是一個二分圖。

這樣的話邊數最多不會超過2N22N-2了。

我們在建好的新圖上用暴力的方法跑就可以了,複雜度爲O(N2)O(N^2)

注意這道題輸入數據很大,要寫個讀入優化才能過。

參考代碼

#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;

const int Maxn = 5000;
const int Maxm = 500000;

struct DSU {
	int fa[Maxn + 5];
	void init(int n) {
		for(int i = 1; i <= n; i++)
			fa[i] = i;
	}
	int find(int u) {
		return u == fa[u] ? u : fa[u] = find(fa[u]);
	}
	void unite(int u, int v) {
		u = find(u), v = find(v);
		if(u == v) return;
		fa[u] = v;
	}
	bool same(int u, int v) {
		return find(u) == find(v);
	}
};

int N, M;
char tag[Maxn + 5];
bool f[Maxn + 5][Maxn + 5];

DSU s;

vector<int> G[Maxn + 5];
void addedge(int u, int v) {
	G[u].push_back(v), G[v].push_back(u);
}

vector<int> G1[Maxn + 5];
void addedge1(int u, int v) {
	if(u == v) G1[u].push_back(v);
	else G1[u].push_back(v), G1[v].push_back(u);
}

queue<pair<int, int> > q;
int col[Maxn + 5];
bool has_odd;
void DFS(int u, int c) {
	col[u] = c;
	for(int i = 0; i < (int)G[u].size(); i++) {
		int v = G[u][i];
		if(tag[u] != tag[v]) continue;
		if(col[u] == col[v]) has_odd = true;
		if(col[v]) continue;
		addedge1(u, v);
		DFS(v, c ^ 1);
		f[u][v] = f[v][u] = true;
		q.push(make_pair(u, v));
	}
}
void rebuild() {
	for(int i = 1; i <= N; i++)
		if(!col[i]) {
			has_odd = false;
			DFS(i, 2);
			if(has_odd) addedge1(i, i);
		}
}

void BFS() {
	while(!q.empty()) {
		int u = q.front().first, v = q.front().second;
		q.pop();
		for(int i = 0; i < (int)G1[u].size(); i++)
			for(int j = 0; j < (int)G1[v].size(); j++) {
				int tu = G1[u][i], tv = G1[v][j];
				if(f[tu][tv]) continue;
				if(tag[tu] != tag[tv]) continue;
				f[tu][tv] = f[tv][tu] = true;
				q.push(make_pair(tu, tv));
			}
	}
}

inline int read() {
	char ch = getchar();
	int ret = 0;
	bool w = false;
	while(ch > '9' || ch < '0') {
		if(ch == '-') w = true;
		ch = getchar();
	}
	while(ch >= '0' && ch <= '9') {
		ret = ret * 10 + (ch ^ 48);
		ch = getchar();
	}
	return w ? -ret : ret;
}

int main() {
#ifdef LOACL
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	N = read(), M = read();
	int Q = read();
	scanf("%s", tag + 1);
	s.init(N);
	for(int i = 1; i <= M; i++) {
		int u = read(), v = read();
		addedge(u, v);
		if(tag[u] != tag[v]) {
			if(s.same(u, v)) continue;
			addedge1(u, v);
			s.unite(u, v);
		}
	}
	rebuild();
	for(int i = 1; i <= N; i++)
		f[i][i] = true, q.push(make_pair(i, i));
	BFS();
	while(Q--) {
		int u = read(), v = read();
		puts(f[u][v] ? "YES" : "NO");
	}
	return 0;
}
發佈了155 篇原創文章 · 獲贊 84 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章