题意
给一个长度为n的字符串s,字符集大小为m。
再建立一张n个点的图,其中串中第i个字符就对应图中第i个点。
对这张图连这样两种边:
若对于i,j,满足|i-j|<=1,在点i和点j之间连双向边。
若对于i,j,满足s[i]=s[j],在点i和点j之间连双向边。
定义dis[i,j]表示点i和点j之间的最短路长度。
定义图的直径为max(dis[i,j]),求该图的直径和有多少对(i,j)满足dis[i,j]等于图的直径。
n<=100000,m<=8
分析
比较牛逼的一道题。
很容易想到可以通过建虚点来优化建图。也就是对每种字符建一个点,然后点i向第s[i]个虚点连一条长度为1的单向边,第s[i]个虚点向点i连一条长度为0的单向边。
设dis1[i,c]表示从i出发到达颜色c中任意一个点的最短路,dis2[c1,c2]表示从颜色c1任意一个点到颜色c2任意一个点的最短路。由于是01最短路,这两样东西可以通过bfs在O(nm)的复杂度内求出
性质1:对于任意的i和j,都有
dis[i,j]<=2m−1
d
i
s
[
i
,
j
]
<=
2
m
−
1
。
证明:因为i到j的最短路上每种颜色最多出现两次,故最短路必然不大于2m-1。
性质2:对于任意的i和c,都有
dis2[s[i],c]<=dis1[i,c]<=dis2[s[i],c]+1
d
i
s
2
[
s
[
i
]
,
c
]
<=
d
i
s
1
[
i
,
c
]
<=
d
i
s
2
[
s
[
i
]
,
c
]
+
1
证明:根据定义可得。
性质3:
dis[i,j]=min(|i−j|,min(dis1[i,c]+dis1[j,c]+1))
d
i
s
[
i
,
j
]
=
m
i
n
(
|
i
−
j
|
,
m
i
n
(
d
i
s
1
[
i
,
c
]
+
d
i
s
1
[
j
,
c
]
+
1
)
)
证明:若i到j的最短路不经过跳跃,则为
|i−j|
|
i
−
j
|
,否则枚举跳跃点c,就是
dis1[i,c]+dis1[j,c]+1
d
i
s
1
[
i
,
c
]
+
d
i
s
1
[
j
,
c
]
+
1
。
知道了上面的性质后,我们把每个点看做一个m维向量,其中向量的第c维为
dis1[i,c]−dis2[s[i],c]
d
i
s
1
[
i
,
c
]
−
d
i
s
2
[
s
[
i
]
,
c
]
。不难发现向量的种类只有
m2m
m
2
m
个,然后两点间通过跳跃得到的最短路可以通、通过向量来表示。
可以枚举直径的右端点i,然后对于
j(|i−j|<=2m−1)
j
(
|
i
−
j
|
<=
2
m
−
1
)
我们暴力计算dis[i,j]。对于
j(|i−j|>=2m)
j
(
|
i
−
j
|
>=
2
m
)
,他们之间的最短路必然要经过跳跃。这时的j有O(n)个,但向量的却只有
O(m2m)
O
(
m
2
m
)
种。那么我们可以枚举向量,然后统计答案即可。
这里可以先预处理好任意两个向量之间的距离,然后就可以每次O(1)得到了。
复杂度
O(m34m+nm2m)
O
(
m
3
4
m
+
n
m
2
m
)
。
看上去很慢,但实际上是跑不满的。
代码
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define mp(x,y) make_pair(x,y)
#define pb(x) push_back(x)
using namespace std;
typedef long long LL;
typedef pair<int,int> pi;
const int N=1000020;
const int inf=1000000000;
int n,m,cnt,last[N],bin[10],dis1[N][8],dis2[8][8],dis3[8][1<<8][8][1<<8],size[8][1<<8],s[N],a[N];
struct edge{int to,next,w;}e[N*4];
queue<pi> que;
char str[N];
vector<int> vec[8];
void addedge(int u,int v,int w)
{
e[++cnt].to=v;e[cnt].w=w;e[cnt].next=last[u];last[u]=cnt;
}
int get_dis(int c1,int s1,int c2,int s2)
{
int ans=inf;
for (int i=0;i<m;i++)
{
int x=dis2[c1][i]+((bin[i]&s1)>0),y=dis2[c2][i]+((bin[i]&s2)>0);
ans=min(ans,x+y+1);
}
return ans;
}
void fill(int x,int y)
{
for (int i=last[x];i;i=e[i].next)
if (e[i].w==0&&dis1[e[i].to][y]==inf)
dis1[e[i].to][y]=dis1[x][y],que.push(mp(e[i].to,y));
}
void bfs()
{
while (!que.empty())
{
int x=que.front().first,y=que.front().second;que.pop();
for (int i=last[x];i;i=e[i].next)
if (dis1[e[i].to][y]==inf)
{
dis1[e[i].to][y]=dis1[x][y]+e[i].w;
que.push(mp(e[i].to,y));fill(e[i].to,y);
}
}
}
int main()
{
scanf("%d%s",&n,str+1);m=8;
bin[0]=1;
for (int i=1;i<=m;i++) bin[i]=bin[i-1]*2;
for (int i=1;i<=n;i++)
{
s[i]=str[i]-'a';
addedge(i,n+s[i]+1,1);addedge(n+s[i]+1,i,0);
if (i<n) addedge(i,i+1,1),addedge(i+1,i,1);
}
for (int i=1;i<=n+m;i++)
{
for (int j=0;j<m;j++) dis1[i][j]=inf;
if (i<=n) dis1[i][s[i]]=0,fill(i,s[i]),que.push(mp(i,s[i]));
}
bfs();
for (int j=0;j<m;j++)
{
for (int k=0;k<m;k++) dis2[j][k]=inf;
for (int i=1;i<=n;i++)
if (s[i]==j)
for (int k=0;k<m;k++) dis2[j][k]=min(dis2[j][k],dis1[i][k]);
}
for (int c1=0;c1<m;c1++)
for (int s1=0;s1<bin[m];s1++)
for (int c2=0;c2<m;c2++)
for (int s2=0;s2<bin[m];s2++)
dis3[c1][s1][c2][s2]=get_dis(c1,s1,c2,s2);
int mx=0;LL ans=0;
for (int i=1;i<=n;i++)
{
for (int j=0;j<m;j++) a[i]+=bin[j]*(dis1[i][j]-dis2[s[i]][j]);
for (int j=1;j<=m*2-1;j++)
{
if (i-j<1) break;
int d=min(j,dis3[s[i]][a[i]][s[i-j]][a[i-j]]);
if (d>mx) mx=d,ans=1;
else if (d==mx) ans++;
}
for (int j=0;j<m;j++)
for (int k=0;k<vec[j].size();k++)
{
int now=vec[j][k],d=dis3[s[i]][a[i]][j][now];
if (d==inf) continue;
if (d>mx) mx=d,ans=size[j][now];
else if (d==mx) ans+=size[j][now];
}
if (i-m*2+1<1) continue;
int c=s[i-m*2+1],now=a[i-m*2+1];
if (!size[c][now]) vec[c].pb(now);
size[c][now]++;
}
printf("%d %I64d",mx,ans);
return 0;
}