Codeforces 718E Matvey's Birthday bfs

题意

给一个长度为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]<=2m1 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(|ij|,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的最短路不经过跳跃,则为 |ij| | 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(|ij|<=2m1) j ( | i − j | <= 2 m − 1 ) 我们暴力计算dis[i,j]。对于 j(|ij|>=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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值