CodeForces 718E Matvey’s Birthday
題目大意
今天與 CF 的連接怎麼這麼穩定???
給定一個長度爲的字符串,字符集爲小寫字母到,我們可以按照如下方式構造出一個無向圖:
- 若,則在點和點之間連一條長度爲的邊;
- 若,則在點和點之間連一條長度爲的邊。
若爲之間的最短路徑,則定義這張圖的直徑爲。
求這張圖的直徑和等於直徑的點對數量。
分析
結論: 是不會超過的。
證明: 我們可以發現,在從到的最短路徑上,一種字母一定不會出現超過兩次。若出現了多於兩次的字母,我們可以發現只需經過原來路徑上的第一個和最後一個(因爲它們直接有邊相連)就可以使得路徑變短。這樣,由於最多隻有種字母,則最長的長度爲。
則我們考慮定義爲從出發,到達某個顏色是的位置的最短距離,這個可以用 BFS 做出來。這部分 比較簡單 就不詳細解釋了。
考慮如何用來表示:
- 只經過第一種類型的邊(即時連上的邊),這樣這個距離就是;
- 經過第二種類型的邊,我們可以考慮通過某個顏色爲中轉點,這樣這個距離就是。
綜上:
這樣一來我們就可以在的時間內求出。
但是有,我們不能夠直接暴力。
考慮計算一個新的值表示從某個顏色爲的節點到某個顏色爲的節點的最短距離,這個值可以在做 BFS 時順帶着算了。
對於的情況,我們直接採用暴力。
但對於時,我們可以發現,不是等於就是等於,又由於只有最多情況,所以我們可以將這個壓成一個集合,若的第位爲則表示,反之就表示。
那麼我們可以再用的複雜度來做一個預處理。設表示一個顏色爲、狀態爲和另外一個顏色爲、狀態爲時的點的合併時的最小結果。
那麼我們在統計答案時直接調用我們計算出來的即可。
總時間複雜度爲。
參考代碼
#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;
}