【CodeForces】【BFS】【狀壓】718E Matvey's Birthday

CodeForces 718E Matvey’s Birthday

題目大意

◇題目傳送門◆

今天與 CF 的連接怎麼這麼穩定???

給定一個長度爲NN的字符串ss,字符集爲小寫字母aahh,我們可以按照如下方式構造出一個無向圖:

  • ij1|i-j|\le 1,則在點ii和點jj之間連一條長度爲11的邊;
  • si=sjs_i=s_j,則在點ii和點jj之間連一條長度爲11的邊。

d(i,j)d(i,j)i,ji,j之間的最短路徑,則定義這張圖的直徑爲max1i,jN,ij{d(i,j)}\max_{1\le i,j\le N,i\neq j}\{d(i,j)\}

求這張圖的直徑和d(i,j)d(i,j)等於直徑的點對數量。

分析

結論: d(i,j)d(i,j)是不會超過1515的。

證明: 我們可以發現,在從iijj的最短路徑上,一種字母一定不會出現超過兩次。若出現了多於兩次的字母,我們可以發現只需經過原來路徑上的第一個和最後一個(因爲它們直接有邊相連)就可以使得路徑變短。這樣,由於最多隻有88種字母,則最長的長度爲2×81=152\times 8-1=15

則我們考慮定義f(i,j)f(i,j)爲從ii出發,到達某個顏色是jj的位置的最短距離,這個可以用 BFS 做出來。這部分 比較簡單 就不詳細解釋了。

考慮如何用f(i,j)f(i,j)來表示d(i,j)d(i,j)

  • 只經過第一種類型的邊(即ij1|i-j|\le 1時連上的邊),這樣這個距離就是ij|i-j|
  • 經過第二種類型的邊,我們可以考慮通過某個顏色爲cc中轉點,這樣這個距離就是f(i,c)+1+f(j,c)f(i,c)+1+f(j,c)

綜上:

d(i,j)=min(ij,min1c8{f(i,c)+f(j,c)+1}) d(i,j)=\min(|i-j|,\min_{1\le c\le 8}\{f(i,c)+f(j,c)+1\})

這樣一來我們就可以在O(N2)O(N^2)的時間內求出d(i,j)d(i,j)

但是NN10510^5,我們不能夠直接暴力。

考慮計算一個新的值g(i,j)g(i,j)表示從某個顏色爲ii的節點到某個顏色爲jj的節點的最短距離,這個值可以在做 BFS 時順帶着算了。

對於ij15|i-j|\le 15的情況,我們直接採用暴力。

但對於ij>15|i-j|>15時,我們可以發現,f(i,c)f(i,c)不是等於g(si,c)g(s_i,c)就是等於g(si,c)+1g(s_i,c)+1,又由於cc只有最多88情況,所以我們可以將這個壓成一個集合SS,若SS的第cc位爲00則表示f(i,c)=g(si,c)f(i,c)=g(s_i,c),反之就表示f(i,c)=g(si,c)+1f(i,c)=g(s_i,c)+1

那麼我們可以再用O(N222N)O(N^22^{2N})的複雜度來做一個預處理。設h(i,j,S1,S2)h(i,j,S_1,S_2)表示一個顏色爲ii、狀態爲S1S_1和另外一個顏色爲jj、狀態爲S2S_2時的點的合併時的最小結果。

那麼我們在統計答案時直接調用我們計算出來的hh即可。

總時間複雜度爲O(8N+83×22N+8N×2N)O(8N+8^3\times 2^{2N}+8N\times 2^N)

參考代碼

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

typedef long long ll;
const int Maxn = 100000;
const int Maxk = 8;

int N;
char s[Maxn + 5];

int f[Maxn + 5][Maxk + 5];
int g[Maxk + 5][Maxk + 5];
vector<int> p[Maxk + 5];

void BFS(int col) {
	static bool vis[Maxn + Maxk + 5];
	memset(vis, false, sizeof vis);
	queue<int> q;
	for(int j = 0; j < (int)p[col].size(); j++)
		q.push(p[col][j]), vis[p[col][j]] = true, f[p[col][j]][col] = 0;
	g[col][col] = 0, vis[N + col] = true;
	while(!q.empty()) {
		int u = q.front();
		q.pop();
		if(u != 1 && !vis[u - 1]) {
			vis[u - 1] = true, q.push(u - 1);
			f[u - 1][col] = f[u][col] + 1;
		}
		if(u != N && !vis[u + 1]) {
			vis[u + 1] = true, q.push(u + 1);
			f[u + 1][col] = f[u][col] + 1;
		}
		if(vis[N + (int)s[u] - 'a' + 1]) continue;
		vis[N + (int)s[u] - 'a' + 1] = true;
		g[s[u] - 'a' + 1][col] = f[u][col];
		for(int i = 0; i < (int)p[s[u] - 'a' + 1].size(); i++) {
			int v = p[s[u] - 'a' + 1][i];
			if(vis[v]) continue;
			f[v][col] = f[u][col] + 1;
			vis[v] = true, q.push(v);
		}
	}
}

int cnt[Maxk + 5][(1 << Maxk) + 5];
int dis[Maxk + 5][Maxk + 5][(1 << Maxk) + 5][(1 << Maxk) + 5];
int st[Maxn + 5];

int main() {
#ifdef LOACL
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	scanf("%d", &N);
	scanf("%s", s + 1);
	for(int i = 1; i <= N; i++)
		p[s[i] - 'a' + 1].push_back(i);
	memset(f, 0x3f, sizeof f);
	memset(g, 0x3f, sizeof g);
	for(int i = 1; i <= Maxk; i++)
		BFS(i);
	memset(dis, 0x3f, sizeof dis);
	for(int col1 = 1; col1 <= 8; col1++)
		for(int col2 = 1; col2 <= col1; col2++)
			for(int s1 = 0; s1 < (1 << 8); s1++)
				for(int s2 = 0; s2 < (1 << 8); s2++) {
					for(int col3 = 1; col3 <= 8; col3++)
						dis[col1][col2][s1][s2] = min(dis[col1][col2][s1][s2],
							g[col1][col3] + g[col2][col3] + ((s1 >> (col3 - 1)) & 1)
							+ ((s2 >> (col3 - 1)) & 1) + 1);
					dis[col2][col1][s2][s1] = dis[col1][col2][s1][s2];
				}
	int ans = 0;
	ll sum = 0;
	for(int i = 1; i <= N; i++) {
		for(int j = max(1, i - 15); j < i; j++) {
			int tmp = i - j;
			for(int col = 1; col <= 8; col++)
				tmp = min(tmp, f[i][col] + f[j][col] + 1);
			if(ans < tmp) ans = tmp, sum = 0;
			if(ans == tmp) sum++;
		}
		for(int col = 1; col <= 8; col++)
			st[i] |= (f[i][col] - g[s[i] - 'a' + 1][col]) << (col - 1);
		for(int col = 1; col <= 8; col++)
			for(int j = 0; j < (1 << 8); j++)
				if(cnt[col][j]) {
					int tmp = dis[col][s[i] - 'a' + 1][j][st[i]];
					if(ans < tmp) ans = tmp, sum = 0;
					if(ans == tmp) sum += cnt[col][j];
				}
		if(i > 15) cnt[s[i - 15] - 'a' + 1][st[i - 15]]++;
	}
	printf("%d %lld\n", ans, sum);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章