Codeforces 718E - Matvey's Birthday(思维题)
首先注意到这个图的特殊性:我们对于所有 \(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;
}
Codeforces 718E - Matvey's Birthday(思维题)的更多相关文章
- C. Nice Garland Codeforces Round #535 (Div. 3) 思维题
C. Nice Garland time limit per test 1 second memory limit per test 256 megabytes input standard inpu ...
- Codeforces 515C 题解(贪心+数论)(思维题)
题面 传送门:http://codeforces.com/problemset/problem/515/C Drazil is playing a math game with Varda. Let’ ...
- Codeforces 1188B - Count Pairs(思维题)
Codeforces 题面传送门 & 洛谷题面传送门 虽说是一个 D1B,但还是想了我足足 20min,所以还是写篇题解罢( 首先注意到这个式子里涉及两个参数,如果我们选择固定一个并动态维护另 ...
- Codeforces 1365G - Secure Password(思维题)
Codeforces 题面传送门 & 洛谷题面传送门 首先考虑一个询问 \(20\) 次的方案,考虑每一位,一遍询问求出下标的这一位上为 \(0\) 的位置上值的 bitwise or,再一遍 ...
- Codeforces 1129E - Legendary Tree(思维题)
Codeforces 题面传送门 & 洛谷题面传送门 考虑以 \(1\) 为根,记 \(siz_i\) 为 \(i\) 子树的大小,那么可以通过询问 \(S=\{2,3,\cdots,n\}, ...
- CodeForces - 427A (警察和罪犯 思维题)
Police Recruits Time Limit: 1000MS Memory Limit: 262144KB 64bit IO Format: %I64d & %I64u Sub ...
- codeforces 848B Rooter's Song 思维题
http://codeforces.com/problemset/problem/848/B 给定一个二维坐标系,点从横轴或纵轴垂直于发射的坐标轴射入(0,0)-(w,h)的矩形空间.给出点发射的坐标 ...
- Codeforces 729D Sea Battle(简单思维题)
http://codeforces.com/contest/738/problem/D https://www.cnblogs.com/flipped/p/6086615.html 原 题意:海战 ...
- @codeforces - 718E@ Matvey's Birthday
目录 @description@ @solution@ @accepted code@ @detail@ @description@ 给定一个长度为 n 的字符串 s,保证只包含前 8 个小写字母 ' ...
随机推荐
- ubuntu修改软件源的方法
最快方法--替换法 刚安装好的ubutun,打开source.list后,用vim替换的方法将所有的us提付出替换为 cn,然后保存退出,更新即可. # vim /etc/apt/source.lis ...
- 23.合并k个有序链表
合并 k 个排序链表,返回合并后的排序链表.请分析和描述算法的复杂度. 示例: 输入: [ 1->4->5, 1->3->4, 2->6 ] 输出: 1-&g ...
- [no code][scrum meeting] Alpha 8
项目 内容 会议时间 2020-04-14 会议主题 API文档第一版交付 会议时长 30min 参会人员 PM+OCR组成员 $( "#cnblogs_post_body" ). ...
- 六步教你如何用PADS进行PCB设计?
在使用PADS进行PCB设计的过程中,需要对印制板的设计流程以及相关的注意事项进行重点关注,这样才能更好的为工作组中的设计人员提供系统的设计规范,同时也方便设计人员之间进行相互的交流和检查. 02 设 ...
- 从0到1使用Kubernetes系列(四):搭建第一个应用程序
传统Kubernetes应用搭建 创建Namespace 在一个Kubernetes集群中可以创建多个Namespace进行"环境隔离",当项目和人员众多的时候,可以考虑根据项目的 ...
- 集合先从ArrayList开始
本篇文章非常建议直接从经典Demo开始哦~ 一.ArrayList简介 ArrayList 的底层是数组队列,相当于动态数组.与 Java 中的数组相比,它的容量能动态增长.在添加大量元素前,应用程序 ...
- Python课程笔记(十一)
一.线程与多线程 1.线程与进程 线程指的是 进程(运行中的程序)中单一顺序的执行流. 多个独立执行的线程相加 = 一个进程 多线程程序是指一个程序中包含有多个执行流,多线程是实现并发机制的一种有效手 ...
- 2021CCPC华为云挑战赛 部分题题解
CDN流量调度问题 题看了没多久就看出来是\(DP\)的题,然后就设了状态\(f[i][j]\)表示到前\(i\)个点时已经用了\(j\)个节点的最小总代价,结果发现转移时\(O(nm^2)\),但这 ...
- 就因为把int改成Integer,第2天被辞了
本文节选自<设计模式就该这样学>之享元模式(Flyweight Pattern) 1 故事背景 一个程序员就因为改了生产环境上的一个方法参数,把int型改成了Integer类型,因为涉及到 ...
- DeWeb第2个通用化模块:主控模块。 手机/电脑自适应。通过修改配置文件即可实现进入不同模块
演示: https://delphibbs.com/main.dw 也可以通过 https://delphibbs.com/login.dw 采用admin/123456登录后自动进入 开发环境和源代 ...