[Codeforces Round #373 DIV1E (CF718E)] Matvey's Birthday

来源:互联网 发布:田丰 阿里云 领英 编辑:程序博客网 时间:2024/06/08 04:47

题意

给定一个字符串,字符集大小为8
将每个位置当做一个点,相邻位置有长度为1的连边,任意相同字母的位置之间有长度为1的连边。求图的直径和直径的长度。
n105

题解

求图的直径需要求任意两点间的最短路,这是不现实的。

DIS(i,j)为位置i与位置j之间的最短路,那么

DIS(i,j)=min{|ij|,min{dis(i,c)+1+dis(j,c)}}

其中dis(i,c)表示从某个位置到达字符x的最短距离。

为什么dis(i,c)+1+dis(j,c)里面要+1呢?因为如果i,j都到达了同一个c,那么这个长度一定大于等于|ij|

同时我们能得到这样的结论,树的直径小于字符集大小的二倍,即15,这个串就是一个例子aabbccddeeffgghh

枚举i,同时枚举j

  1. |ij|<16,此时直接由DIS(i,j)=min{|ij|,min{dis(i,c)+1+dis(j,c)}}求出
  2. |ij|16,不能直接枚举j了,此时DIS(i,j)=min{dis(i,c)+1+dis(j,c)}

现在设法求出|ij|16时的max{DIS(i,j)},注意此时的i是固定的,j是不确定的。

再设d(c1,c2)代表字符c1c2之间的最短距离。那么d(sj,c)dis(j,c)d(sj,c)+1,也就是要么sjc之间最短路恰好是j位置到c的最短路,要么需要再跳一步。

预处理求出dis数组与d数组,再设一个mask数组,mask(j)对于每一个位置j,用二进制存下dis(j,c)d(sj,c),0c<8
再设数组cnt(x,mask)来统计j(|ij|16),sj=xmask(j)=mask的数量。

i确定时先确定sj再枚举mask,此时

min{dis(i,c)+1+dis(j,c)}=min{dis(i,c)+1+d(sj,c)+maskc}

这样就能求出i,sj,mask确定时的最短路并更新答案。
时间复杂度O(n|C|3+n|C|22|C|),其中|C|为字符集大小。

代码

/// by ztx/// blog.csdn.net/hzoi_ztx#define Rep(i,l,r) for(i=(l);i<=(r);i++)#define rep(i,l,r) for(i=(l);i< (r);i++)#define Rev(i,r,l) for(i=(r);i>=(l);i--)#define rev(i,r,l) for(i=(r);i> (l);i--)#define Each(i,v)  for(i=v.begin();i!=v.end();i++)#define r(x)   read(x)typedef long long ll ;typedef double lf ;int CH , NEG ;template <typename TP>inline void read(TP& ret) {    ret = NEG = 0 ; while (CH=getchar() , CH<'!') ;    if (CH == '-') NEG = true , CH = getchar() ;    while (ret = ret*10+CH-'0' , CH=getchar() , CH>'!') ;    if (NEG) ret = -ret ;}#define  maxn  100010LL#define  infi  0x3f3f3f3fLL#define  id(c) (c+n+1)int n, ans;ll cnt;int dis[maxn][8], d[8][8], mask[maxn],c[8][256];char s[maxn];std::queue<int> q;inline void bfs(int x) {    int i, u, v;    dis[id(x)][x] = 0;    Rep (i,1,n) if (s[i] == x)        dis[i][x] = 0, q.push(i);    while (!q.empty())        if (u = q.front(), q.pop(), u <= n) {            if (v = u+1, 1<=v && v<=n && dis[v][x]==infi) {                dis[v][x] = dis[u][x]+1, q.push(v);                if (dis[id(s[v])][x] == infi)                    dis[id(s[v])][x] = dis[u][x]+1, q.push(id(s[v]));            }            if (v = u-1, 1<=v && v<=n && dis[v][x]==infi) {                dis[v][x] = dis[u][x]+1, q.push(v);                if (dis[id(s[v])][x] == infi)                    dis[id(s[v])][x] = dis[u][x]+1, q.push(id(s[v]));            }        }        else Rep (i,1,n) if (id(s[i])==u && dis[i][x]==infi)            dis[i][x] = dis[u][x]+1, q.push(i);}int main() {    int i, j, k, l, now, x;    r(n), scanf("%s", s+1);    Rep (i,1,n) s[i] -= 'a';    memset(dis, 0x3f, sizeof dis);    while (!q.empty()) q.pop();    rep (i,0,8) {        bfs(i);        rep (x,0,8) d[x][i] = dis[id(x)][i];    }    Rep (i,1,n) rep (x,0,8)        if (dis[i][x] > d[s[i]][x]) mask[i] |= 1<<x;    Rep (i,1,n) {        rep (j,std::max(i-15,1),i) {            now = i-j;            rep (k,0,8) now = std::min(now, dis[j][k]+dis[i][k]+1);            if (now == ans) cnt ++ ;            if (now > ans) ans = now, cnt = 1;        }        int t = i-16;        if (t >= 1) c[s[t]][mask[t]] ++ ;        rep (x,0,8) rep (k,0,256)            if (c[x][k]) {                now = infi;                rep (l,0,8) now = std::min(now, d[x][l]+1+dis[i][l]+(k&(1<<l)));                if (now == ans) cnt += c[x][k];                if (now > ans) ans = now, cnt = c[x][k];            }    }    printf("%d %lld\n", ans, cnt);    END: getchar(), getchar();    return 0;}
0 0