@description@

给定一个长度为 n 的字符串 s,保证只包含前 8 个小写字母 'a', 'b', ... 'h'。

根据该字符串建一个图。两个点 p, q 之间有连边要么 |p - q| = 1,要么 s[p] = s[q]。

求该图直径的长度(所有点对之间的最短距离的最大值),以及直径的数量。

Input

第一行一个整数 n,表示字符串长度。

第二行一个字符串 s。保证只包含前 8 个小写字母。

Output

输出直径长度与直径数量。

Examples

Input

3

abc

Output

2 1

Input

7

aaabaaa

Output

2 4

@solution@

虽然 cf 的难度系统不太准,但至少难度 > 3000 都是我不会做的题.jpg。

先考虑如何快速求两个点 i, j 之间的最短路。

首先注意到最短路上不能出现不相邻的相同字符(即类似于 'a' -> ... -> 'a'),否则我可以直接从第一个相同字符跳到最后一个相同字符。

这意味着最短路径一定 <= 2*8。

假如不经过相邻的相同字符(即不经过 s[p] = s[q] 类型的边),最短路径长度为 |i - j|。

否则,我们以某种字符为中转,向两边求到 i, j 的最短路,两者之和即 i->j 的最短路。

即如果记 d[c][i] 表示 i 到达某一个字符 c 的最短路,此时最短路径为 min(d[c][i] + d[c][j])。

那么 i, j 之间的最短路一定为 min(|i - j|, min(d[c][i] + d[c][j]))。

怎么求 d[c][i]?我们可以 bfs 搞定。

只是需要注意由于相同字符构成了一个完全图,假如这个字符的所有点已经全部进入队列,我们需要打上 tag 防止之后反复访问。不然时间就炸了。

考虑通用的解法:枚举 i,求以 i 为起点的最长路径及路径数量。但是这样子还是 O(n^2) 的。

首先,只有 i 前面 16 个以及后面 16 个是可能取 |i - j| 为最小值的,这些直接暴算。

那么剩下的 j 只可能取 min(d[c][i] + d[c][j]),我们再研究怎么简化这一部分的复杂度。

注意到这个只跟 d[c][j] 有关。我们或许可以将 d[0][j], d[1][j], ... 相同的 j 放在一起处理。

具体怎么操作?注意到相同字符 x 对应的 j, k,总有 |d[c][j] - d[c][k]| <= 1(因为它们之间有边连接)。

记 mnd[c][x] = min(d[c][j]),那么对于字符 x 对应的任意 j,有 d[c][j] = mnd[c][x] 或 d[c][j] = mnd[c][x] + 1。

我们可以用一个 8 位的二进制状态将 d[c][j] 压缩,并存储该二进制状态对应的数量,就可以实现我们的目的了。

枚举 i 过后,再枚举 x 以及 8 位的二进制状态得到 d[0][j], d[1][j], ...,然后枚举中转字符 c,根据式子算。

注意我们需要把 i 前面 16 个以及后面 16 个的贡献先消掉。不然会计算重复。

@accepted code@

#include <queue>
#include <cstdio>
#include <vector>
#include <algorithm>
using namespace std;
const int MAXN = 100000;
int clr[MAXN + 5], n;
vector<int>v[MAXN + 5];
int d[10][MAXN + 5];
bool tag[10];
void get_dist(int x) {
queue<int>que;
for(int i=0;i<8;i++) tag[i] = false;
for(int i=1;i<=n;i++) d[x][i] = MAXN + 5;
for(int i=0;i<v[x].size();i++)
d[x][v[x][i]] = 0, que.push(v[x][i]);
while( !que.empty() ) {
int f = que.front(); que.pop();
if( !tag[clr[f]] ) {
tag[clr[f]] = true;
for(int i=0;i<v[clr[f]].size();i++) {
int u = v[clr[f]][i];
if( d[x][u] > d[x][f] + 1 )
d[x][u] = d[x][f] + 1, que.push(u);
}
}
if( f != 1 && d[x][f-1] > d[x][f] + 1 )
d[x][f-1] = d[x][f] + 1, que.push(f-1);
if( f != n && d[x][f+1] > d[x][f] + 1 )
d[x][f+1] = d[x][f] + 1, que.push(f+1);
}
}
int mnd[10][10], bts[MAXN + 5], cnt[10][1<<10];
void get_mask(int x) {
for(int i=0;i<8;i++) {
mnd[x][i] = MAXN + 5;
for(int j=0;j<v[x].size();j++)
mnd[x][i] = min(mnd[x][i], d[i][v[x][j]]);
for(int j=0;j<v[x].size();j++)
bts[v[x][j]] |= ((d[i][v[x][j]] - mnd[x][i])<<i);
}
for(int j=0;j<v[x].size();j++)
cnt[x][bts[v[x][j]]]++;
}
int ans1; long long ans2;
void update(int x, int t) {
if( x == ans1 ) ans2 += t;
else if( x > ans1 ) ans1 = x, ans2 = t;
}
char s[MAXN + 5];
int abs(int x) {return x >= 0 ? x : -x;}
int main() {
scanf("%d%s", &n, s + 1);
for(int i=1;i<=n;i++)
v[s[i]-'a'].push_back(i), clr[i] = s[i] - 'a';
for(int i=0;i<8;i++) get_dist(i);
for(int i=0;i<8;i++) get_mask(i);
ans1 = 0, ans2 = 0;
int t = (1<<8);
for(int i=1;i<=n;i++) {
for(int j=max(1,i-16);j<=min(i+16,n);j++) {
int mn = abs(i - j);
for(int k=0;k<8;k++)
mn = min(mn, d[k][i] + 1 + d[k][j]);
cnt[clr[j]][bts[j]]--, update(mn, 1);
}
for(int j=0;j<8;j++) {
for(int s=0;s<t;s++) {
int mn = MAXN + 5;
for(int k=0;k<8;k++)
mn = min(mn, mnd[j][k] + ((s>>k) & 1) + d[k][i] + 1);
if( cnt[j][s] )
update(mn, cnt[j][s]);
}
}
for(int j=max(1,i-16);j<=min(i+16,n);j++)
cnt[clr[j]][bts[j]]++;
}
printf("%d %lld\n", ans1, ans2/2);
}

@detail@

因为卡在第一步(求两个点之间的最短路)所以。。。也没什么好说的吧。。。

感觉自己代码能力还是有所提升的样子(好久没有那种一道题调一天的快感了)。

这个 detail 的板块最近基本也没用来提示代码细节,反倒是在吐槽了 2333。

@codeforces - 718E@ Matvey's Birthday的更多相关文章

  1. Codeforces 718E - Matvey's Birthday(思维题)

    Codeforces 题面传送门 & 洛谷题面传送门 首先注意到这个图的特殊性:我们对于所有 \(s_i=s_j\)​ 的 \((i,j)\)​ 之间都连了条边,而字符集大小顶多只有 \(8\ ...

  2. 【codeforces 718E】E. Matvey's Birthday

    题目大意&链接: http://codeforces.com/problemset/problem/718/E 给一个长为n(n<=100 000)的只包含‘a’~‘h’8个字符的字符串 ...

  3. python爬虫学习(5) —— 扒一下codeforces题面

    上一次我们拿学校的URP做了个小小的demo.... 其实我们还可以把每个学生的证件照爬下来做成一个证件照校花校草评比 另外也可以写一个物理实验自动选课... 但是出于多种原因,,还是绕开这些敏感话题 ...

  4. 【Codeforces 738D】Sea Battle(贪心)

    http://codeforces.com/contest/738/problem/D Galya is playing one-dimensional Sea Battle on a 1 × n g ...

  5. 【Codeforces 738C】Road to Cinema

    http://codeforces.com/contest/738/problem/C Vasya is currently at a car rental service, and he wants ...

  6. 【Codeforces 738A】Interview with Oleg

    http://codeforces.com/contest/738/problem/A Polycarp has interviewed Oleg and has written the interv ...

  7. CodeForces - 662A Gambling Nim

    http://codeforces.com/problemset/problem/662/A 题目大意: 给定n(n <= 500000)张卡片,每张卡片的两个面都写有数字,每个面都有0.5的概 ...

  8. CodeForces - 274B Zero Tree

    http://codeforces.com/problemset/problem/274/B 题目大意: 给定你一颗树,每个点上有权值. 现在你每次取出这颗树的一颗子树(即点集和边集均是原图的子集的连 ...

  9. CodeForces - 261B Maxim and Restaurant

    http://codeforces.com/problemset/problem/261/B 题目大意:给定n个数a1-an(n<=50,ai<=50),随机打乱后,记Si=a1+a2+a ...

随机推荐

  1. JDK8日期时间操作小汇总

    统一使用java.time.*包下的类 1.获取当前的日期.时间.日期加时间 LocalDate todayDate = LocalDate.now(); //今天的日期 LocalTime now ...

  2. Apache Flink 1.9重磅发布!首次合并阿里内部版本Blink重要功能

    8月22日,Apache Flink 1.9.0 版本正式发布,这也是阿里内部版本 Blink 合并入 Flink 后的首次版本发布.此次版本更新带来的重大功能包括批处理作业的批式恢复,以及 Tabl ...

  3. Vue.extend用法

    Vue.extend 是构造一个组件的语法器. 用法 Vue.extend ( options ),options 是对象: 使用基础Vue构造器,创建一个子类,参数是一个包含组件选项的对象,data ...

  4. php+js实现百度地图多点标注的方法

    本文实例讲述了php+js实现百度地图多点标注的方法.分享给大家供大家参考,具体如下: 1.php创建json数据 ? 1 2 $products = $this->product_db-> ...

  5. WPF内嵌CEF控件,与JS交互

    1)安装cefsharp.winform包 打开VS2017,打开nuget,找到cefsharp.winform,安装 问:为什么wpf程序不使用cefsharp.wpf? 答:因为cefwpf 4 ...

  6. Xshell 、PuTTY 复制文件到Linux

    一.使用Xshell 在linux下下载一个需要安装一个工具lrzsz包: [root@localhost home] # yum install -y lrzsz 从windows上传文件到linu ...

  7. jnhs-SpringMVC jsp页面向controller传递参数的五种方式

    一共是五种传参方式: 一:直接将请求参数名作为Controller中方法的形参 public  String login (String username,String password)   : 解 ...

  8. 避免SQL注入三慷慨法

    版权声明:本文为博主原创文章,未经博主同意不得转载. https://blog.csdn.net/wangyy130/article/details/26154837       要说SQL注入还要从 ...

  9. oracle-视图-索引-序列

    Oracle提高查询性能 一 视图 视图是一个虚拟表,就是对select查询的结果取个名字.其内容由查询定义.同真实的表一样,视图包含一系列带有名称的列和行数据.但是,视图并不在数据库中以存储的数据值 ...

  10. day37 01-上次课内容回顾