codeforces 718E. Matvey's Birthday

题目链接http://codeforces.com/contest/718/problem/E
题目大意:给定一个长度为n的只包含a~h的字符串s,对于1<=a,b<=n,,如果|a-b|=1或s[a]=s[b],就在a与b之间连一条无向边,求这个图的直径以及有多少对点的距离(这里指最短距离)等于这个直径。直径即一个图中距离最大的两个点之间的距离。
数据范围:2<=n<=100 000

题解:设 f [ i ][ c ] 表示从点 i 走到字母为 c 的点的最短距离,这个可以通过枚举 c 然后 bfs 得到,时间复杂度O (n*sigma) 或 O (n*sigma^2) 。对于两个点 i , j ,它们的最短路有两种情况:
1.最短路上只经过相邻点的连边.
2.最短路上有经过同种字母之间的连边.
于是我们可以知道i和j的距离等于min(|i - j| , min(f[i][c]+1+f[j][c]))。

再设dist[c1][c2]为字母为c1和c2的点的最小距离,显然dist[s[i]][c]<=f[i][c]<=dist[s[i]][c]+1,所以我们计算每个点的状态mark[ i ],其中mark[ i ][c]=f[i][c]-dist[s[i]][c],于是我们就可以从点和点的距离(通过mark)变成点和字母的距离啦,至于为什么不是字母和字母的距离,那是因为这样就不能排除情况1的最短路了。

到这里解决方法就差不多出来了,我们从1到n枚举点 i ,然后算出在 i 之前的哪些点与 i 距离属于情况1,这样的点一定是连续的,假设是now~i,然后我们统计1~now-1中的两个值,其中num[x][y]=字母为 x 状态为 z(z包含y,即y&z=y) 的点的个数之和,nums[x]=字母为x的点的个数,时间复杂度O(n*2^sigma)

然后枚举点 i 到字母为 j 的点的距离dst(显然dst=min(f[i][c]+1+dist[c][j]+字母为 j 的点的mark值的第c位))和个数cnt,时间复杂度O(n*sigma^2)
令mindst=min(f[i][c]+1+dist[c][j]),mrk表示状态(其中若f[i][c]+1+dist[c][j]=mindst则mrk[c]=1否则mrk[c]=0),若num[j][mrk]>0那么dst=mindst+1,cnt=num[j][mrk],否则dst=mindst,cnt=nums[j]。然后用dst和cnt更新答案。
最后不要忘了还要判断一下 i 到now的距离。

代码如下:

#include <algorithm>
#include <cstdio>
using namespace std;
const int N=100005;
int d[N],q[N],fi[10],ne[N],f[N][10],dist[10][10],mark[N],
    num[10][1<<8],nums[10],n;
char s[N];
void bfs(int c){
    int h=1,t=0,u[10];
    for (int i=1;i<=n;i++)
        if (s[i]-'a'==c) d[q[++t]=i]=0;else d[i]=-1;
    for (int i=0;i<8;i++) u[i]=1;u[c]=0;
    for (;h<=t;h++){
        if (u[s[q[h]]-'a']){
            u[s[q[h]]-'a']=0;
            for (int i=fi[s[q[h]]-'a'];i;i=ne[i])
                if (d[i]==-1) d[q[++t]=i]=d[q[h]]+1;
        }
        if (q[h]>1 && d[q[h]-1]==-1) d[q[++t]=q[h]-1]=d[q[h]]+1;
        if (q[h]<n && d[q[h]+1]==-1) d[q[++t]=q[h]+1]=d[q[h]]+1;
    }
    for (int i=1;i<=n;i++) f[i][c]=d[i];
}
int main(){
    scanf("%d\n%s",&n,s+1);
    for (int i=1;i<=n;i++){
        ne[i]=fi[s[i]-'a'];fi[s[i]-'a']=i;
    }
    for (int i=0;i<8;i++) bfs(i);
    for (int i=0;i<8;i++)
        for (int j=0;j<8;j++) dist[i][j]=-1;
    for (int i=1,j;i<=n;i++)
        for (j=0;j<8;j++)
        if (f[i][j]!=-1 && (dist[s[i]-'a'][j]==-1 || f[i][j]<dist[s[i]-'a'][j]))
            dist[s[i]-'a'][j]=f[i][j];
    for (int i=1,j;i<=n;i++){
        mark[i]=0;
        for (j=7;j>=0;j--) mark[i]=mark[i]<<1^(f[i][j]-dist[s[i]-'a'][j]);
    }
    int ans=0,now=1;long long ansp=0;
    for (int i=1,j,k;i<=n;i++){
        int l=i;
        for (;l>=1;l--){
            int mindst=-1;
            for (j=0;j<8;j++)
                if (f[l][j]!=-1 && f[i][j]!=-1 && (mindst==-1 || f[l][j]+f[i][j]+1<mindst))
                    mindst=f[l][j]+f[i][j]+1;
            if (mindst<=i-l) break;
        }
        for (;now<=l;now++){
            nums[s[now]-'a']++;
            for (j=mark[now];j;j=(j-1)&mark[now]) num[s[now]-'a'][j]++;
        }
        for (j=0;j<8;j++){
            int mindst=-1,minnum=0;
            for (k=0;k<8;k++)
                if (f[i][k]!=-1 && dist[k][j]!=-1 && (mindst==-1 || f[i][k]+dist[k][j]+1<mindst))
                    mindst=f[i][k]+dist[k][j]+1;
            if (mindst==-1) continue;
            int mrk=0;
            for (k=0;k<8;k++)
                if (f[i][k]!=-1 && dist[k][j]!=-1 && f[i][k]+dist[k][j]+1==mindst) mrk^=1<<k;
            if (num[j][mrk]) mindst++,minnum=num[j][mrk];
                else minnum=nums[j];
            if (!minnum) continue;
            if (mindst>ans) ans=mindst,ansp=minnum;
                else if (mindst==ans) ansp=ansp+minnum;
        }
        if (i-now>ans) ans=i-now,ansp=1;
            else if (i-now==ans) ansp++;
    }
    printf("%d %I64d\n",ans,ansp);
}

--------------------- 本文来自 aufeas 的CSDN 博客 ,全文地址请点击:https://blog.csdn.net/aufeas/article/details/52895953?utm_source=copy

猜你喜欢

转载自blog.csdn.net/c_czl/article/details/82941816