2021年度训练联盟热身训练赛第七场 K题预处理加二分
Posted CCSU_Cola
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了2021年度训练联盟热身训练赛第七场 K题预处理加二分相关的知识,希望对你有一定的参考价值。
题意: 给出n个序列,长度都为m,且顺序是按照字典序排序的,需要你选取一个区间来截取每个字符串,该区间需要满足截取下来的字符串按照字典序重排后的顺序和原字符串的顺序相同。
思路: 既然截取下来的字符串按照字典序重排后的顺序需要和原字符串顺序相同,那么我截取下来的字符串必须是原来排在上面的字符串比下面的字符串字典序更小,我们可以采取枚举区间的左端点L再查找该区间的右端点R即可,找到距离L最近的一点且该位置上当前串的字符小于它的下一个串的字符定为R1,然后根据相同思路枚举1~n-1的串求出R2,R3…R(n-1),最后取最大的Ri则算出的区间满足条件。但每次求出的R有个前提则是在该L和R区间内不可存在上一个串的字符大于下一个字符的情况,否则不合法。
暴力查找R显然不可取,我们可以定义一个vector数组p,先将串1小于串2的所有位置存入p[1],串2小于串3的所有位置存入p[2]中,…。为了满足该区间不能存在上一个串的字符大于下一个字符的情况,我们还需要将串1大于串2的所有位置存入p[1+n]中,串2大于串3的所有位置存入p[2+n]中。然后每次通过二分查询大于L的最近的位置R,再判断是否合法即可。
#include<bits/stdc++.h>
using namespace std;
int n,m;
string str[600];
vector<int>p[1200];
int main(){
scanf("%d%d",&n,&m);
for(int i=0;i<n;i++){
cin>>str[i];
}
for(int i=0;i<m;i++){
for(int j=0;j<n-1;j++){
if(str[j][i]<str[j+1][i]){
p[j].push_back(i);//小于的位置插入
}
else if(str[j][i]>str[j+1][i]){
p[j+n].push_back(i);//大于的位置插入
}
}
}
int ans=0x7fffffff,a,b;
for(int i=0;i<m;i++){
int l=i,r=i,flag=0,kk=0x7fffffff;
//printf("%d %d\\n",l,r);
for(int j=0;j<n-1;j++){
int w=lower_bound(p[j].begin(),p[j].end(),l)-p[j].begin();//上面串小于下面串的最近位置R
if(w>=p[j].size())break;
int ww=lower_bound(p[j+n].begin(),p[j+n].end(),l)-p[j+n].begin();//上面串大于下面串的最近位置
if(ww<w)break;//如果ww<w则该区间不合法
int f=p[j][w];
if(f>r){//当前r大于之前存在的r,更新r
r=f;
}
flag=j;//用来判断是否遍历了n-1个字符
}
int k=r-l;
if(flag!=n-2)continue;
if(k<ans&&flag==n-2){
a=l;
b=r;
ans=k;
}
}
printf("%d %d\\n",a+1,b+1);
}
(如有不对,欢迎各位大佬指点)
以上是关于2021年度训练联盟热身训练赛第七场 K题预处理加二分的主要内容,如果未能解决你的问题,请参考以下文章
[Nowcoder | UPC] 2021年度训练联盟热身训练赛第六场 Hopscotch | 最短路 bfs