Codeforces 题面传送门 & 洛谷题面传送门

首先注意到这个图的特殊性:我们对于所有 \(s_i=s_j\)​ 的 \((i,j)\)​ 之间都连了条边,而字符集大小顶多只有 \(8\)​,因此当 \(n\)​ 比较大时这张图肯定是相当稠密的,故我们猜测这个直径长度肯定也不会太长。事实的确如此,具体来说,对于图上任意两个点 \(i,j\)​,它们之间最短距离的长度肯定不会超过 \(15\)​,具体证明大概就对于每一对字母 \((x,y)\)​,如果存在某两个位置 \(i,i+1\) 满足 \(s_i=x,s_{i+1}=y\) 那么我们就在 \(x,y\) 之间连一条无向边。那么显然得到的大小为 \(s\text{中出现过的字符个数}\) 的图是连通图,故 \(\forall i,j\),\(s_i,s_j\) 在图上的距离不会超过 \(7\),而对于图上的一个大点(也就是每一种字母缩成的一个点),其包含的所有小点两两之间的最短距离恰好为 \(1\),也就是说,对于我们缩点后形成的图,我们假设我们找到了一条路径 \(v_1\to v_2\to\cdots\to v_k\),那么我们最多只用花 \(1\) 的代价实现从 \(v_{k-1}\to v_k\) 这条边到达 \(v_k\to v_{k+1}\) 这条边。因此这个最短距离的上界就是 \(8+7=15\)。

接下来我们考虑探究一下两点 \(i,j\) 之间的最短路是什么。不妨假设 \(i<j\),那么从 \(i\) 到达 \(j\) 有两种选择,要么一直往右走,步数 \(j-i\),要么经过某条连接两个不相邻的点的边,而这可以视作,选择某种字符 \(c\),然后从 \(i\) 走到某个字符为 \(c\) 的点,然后 \(j\) 也走到某个字符为 \(c\) 的点,然后再花费 \(1\) 的代价连接这两个字符为 \(c\) 的点,那么我们记 \(dis_{i,c}\) 表示 \(i\) 到达字符为 \(c\) 的点的最小代价。\(dis_{i,c}\) 可以通过用建虚点的技巧,也就是对于每个字符建一个虚点,然后对于所有字符为 \(c\) 的点,连一条该点到该字符对应的虚点,权值为 \(1\) 的边以及该字符对应的虚点到该点,权值为 \(0\) 的边,再使用多源 01bfs 在 \(\mathcal O(8n)\) 的时间内求出。这样我们即可在 \(\mathcal O(1)\) 的时间内计算两个点的最短距离,即 \(dis(i,j)=\min(j-i,dis_{j,k}+dis_{i,k}+1)\),这样暴力枚举是平方的,不过注意到一个性质,就是如果 \(j-i>15\),那么显然这个 \(\min\) 会取到后者,因此对于 \(j-i\le 15\) 我们考虑暴力枚举,\(j-i>15\) 的情况,注意到如果我们设 \(disc_{c1,c2}\) 为所有 \(s_i=c1\) 的点中 \(dis_{i,c2}\) 的最小值,那么必然有 \(dis_{i,c}-disc_{s_i,c}\in\{0,1\}\)。因此我们考虑在枚举的过程中将这个状态用一个 \(8\) 位二进制数记录下来,具体来说我们对于每个 \(i\) 记录一个 \(8\) 位二进制数 \(S\),\(S\) 的第 \(c\) 位为 \(1\) 表示 \(dis_{i,c}-disc_{s_i,c}=1\),否则 \(dis_{i,c}-disc_{s_i,c}=0\),然后我们在枚举的过程中开一个桶 \(cnt_{j,S}\) 表示前面有多少个 \(s_i=j\) 且 \(j\) 的状态为 \(S\),然后对于前面的答案就暴力对所有 \(j,S\) 批量处理答案即可。

时间复杂度 \(\mathcal O(8192·n)\),由于完全卡不满,可以通过此题。

const int MAXN=1e5;
const int MAXP=256;
const int INF=1061109567;
int n,cnt[MAXP+2][9];char s[MAXN+5];
int dis[MAXN+15][9],disc[9][9],res=0;ll resc=0;
void merge(int x,int y){
// printf("%d %d\n",x,y);
if(x>res) res=x,resc=y;
else if(x==res) resc+=y;
}
int main(){
scanf("%d%s",&n,s+1);
memset(dis,63,sizeof(dis));memset(disc,63,sizeof(disc));
for(int i=0;i<8;i++){
deque<int> q;
for(int j=1;j<=n;j++) if(s[j]-'a'==i) dis[j][i]=0,q.push_back(j);
while(!q.empty()){
int x=q.front();q.pop_front();
if(x<=n){
if(x-1>=1&&dis[x-1][i]==INF) dis[x-1][i]=dis[x][i]+1,q.push_back(x-1);
if(x+1<=n&&dis[x+1][i]==INF) dis[x+1][i]=dis[x][i]+1,q.push_back(x+1);
if(dis[s[x]-'a'+n+1][i]==INF) dis[s[x]-'a'+n+1][i]=dis[x][i]+1,q.push_back(s[x]-'a'+n+1);
} else {
for(int j=1;j<=n;j++) if(s[j]-'a'==x-n-1)
if(dis[j][i]>=dis[x][i]) dis[j][i]=dis[x][i],q.push_front(j);
}
} for(int j=1;j<=n;j++) chkmin(disc[s[j]-'a'][i],dis[j][i]);
// for(int j=1;j<=n;j++) printf("%d%c",dis[j][i]," \n"[j==n]);
}
// for(int i=0;i<8;i++) for(int j=0;j<8;j++)
// printf("%d%c",disc[i][j]," \n"[j==7]);
for(int i=1;i<=n;i++){
for(int j=max(1,i-15);j<=i;j++){
int mn=i-j;
for(int k=0;k<8;k++) chkmin(mn,dis[j][k]+dis[i][k]+1);
merge(mn,1);
} if(i-16>=1){
int msk=0;
for(int j=0;j<8;j++) msk|=(dis[i-16][j]-disc[s[i-16]-'a'][j])<<j;
cnt[msk][s[i-16]-'a']++;
} for(int j=0;j<MAXP;j++) for(int k=0;k<8;k++) if(cnt[j][k]){
int mn=INF;
for(int l=0;l<8;l++) chkmin(mn,dis[i][l]+disc[k][l]+(j>>l&1)+1);
merge(mn,cnt[j][k]);
}
} printf("%d %lld\n",res,resc);
return 0;
}

最新文章

  1. Introduction to Microsoft Dynamics 365 licensing
  2. 添加dubbo xsd的支持
  3. LeetCode OJ学习
  4. DOS下导入dmp文件到Oracle数据库
  5. C++静态库中使用_declspec(dllexport) 不能导出函数的问题
  6. [Sqlite]--&amp;gt;Java采用jdbc联系Sqlite各种特定的工艺数据库的数据操作
  7. [JLOI2015]城池攻占 左偏树
  8. 文件去除git版本控制
  9. PHP content-type为&quot;application/json&quot;的post过来的数据$_POST接受不到的问题
  10. java读取各种类型文件
  11. 小程序解决方案 Westore - 组件、纯组件、插件开发
  12. vuejs的双向绑定实现原理
  13. k8s之创建etcd集群
  14. Xadmin显示视图
  15. 2019年Python数据挖掘就业前景前瞻
  16. python内置函数值 -- chr() ord()
  17. React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined.
  18. Kafka 温故(五):Kafka的消费编程模型
  19. 【linux报错】安装好虚拟机后,挂载光盘报错:mount:you must specify the filesystem type
  20. MQTT-SN协议乱翻之简要介绍

热门文章

  1. 分布式全局ID与分布式事务
  2. 全连接层dense作用
  3. 第31篇-方法调用指令之invokevirtual
  4. [no_code][Beta]发布声明报告
  5. [no code][scrum meeting] Alpha 3
  6. spring security中动态更新用户的权限
  7. 按之字形顺序打印二叉树 牛客网 剑指Offer
  8. uni-app使用wx-canvas实现微信小程序上显示地图map和坐标geo
  9. SpringBoot教程(学习资源)
  10. freeswitch的docker构建过程