LNSYOJ#204谁是天才【RMQ理解+单调栈】【做题报告】
这是一道RMQ水题,因为本蒟蒻居然一A
题目描述
张大牛:“我是天才!”
大肥熊:“你为什么是天才?”
张大牛:“你随便给我一个单词(大小写字母组成)长度为NN,去掉MM个字符后,我能知道字典序最小的字符串是什么样子的”
大肥熊:“换过来,现在假设这个字典序最小的字符串中第aiai个字符在原串中的位置为pospos,那么原串中区间[pos−ki,pos+ki][pos−ki,pos+ki]中字典序最大的字符是什么?”
张大牛又被难倒了。现在这个难倒天才的题目交到你手上了。
注:区间[pos−ki,pos+ki][pos−ki,pos+ki]超过原字符串的边界,按照原字符串的边界处理。原串的整个区间为[1,N][1,N],如果pospos可以取多个值,按照最靠前的位置计算。
输入格式
第一行M,T。MM,T。M如题意,TT为TT组询问。
第二行为一个由大、小写字母组成的字符串。串的长度N(N>M>0)N(N>M>0)。
接下来TT行每行两个正整数ai,ki(1≤ai≤N−M),(1≤ki≤N)ai,ki(1≤ai≤N−M),(1≤ki≤N)。
输出格式
T行,每行对询问给出一个答案。
样例一
input
20 2 abcdefghijklmnopqrstuvwxyz 5 20 3 3
output
y f
限制与约定
对于100%的数据,N≤105,T≤105N≤105,T≤105
时间限制:1s1s
空间限制:256MB
这道题呢,涉及到两个知识点
1.单调栈+贪心
这个比较好理解,但是本蒟蒻还是询问了同桌大佬@lizitong,才明白,这个的思路就是,每次来一个字符串,就从最顶上开始弹,只要顶上比当前大,就弹,然后维护一个cnt,使之只弹m次,最后剩下的一定是最优解,这个是比较纯粹的单调栈
这个的模板好像没有,我写的习惯就是用while循环维护
2.RMQ
RMQ适合于维护区间最值,预处理O(n),询问O(1)
这个属于ST(倍增)算法,就是一次跳2的j次个,和lca比较像,f[i][j]表示的是以i为起点,跳2的j次个,这个区间的最值,询问的时候只要从两个方向覆盖就OK
注意有的地方是否要+1-1,这个也比较好理解
然后呢这道题在单调栈弹栈入栈时候维护下位置就OK,然后再询问,是不是很水?
模板在此
1 for(int i=1;i<=n;i++)f[i][0]=a[1]; 2 for(int j=1;j<=log2(n);j++) 3 for(int i=1;(i+(1<<j)-1)<=n;i++) 4 f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]); 5 lg=log2(r-l+1); 6 printf("%c\n",max(f[l][lg],f[r-(1<<lg)+1][lg]));
代码在此,
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<cmath> 5 using namespace std; 6 int n,m,t,top,cnt,a,k; 7 int pos[100111]; 8 char s[100111],stk[100111],f[100111][20]; 9 int main() 10 { 11 //freopen("misaki.in","r",stdin); 12 scanf("%d%d%s",&m,&t,s+1); 13 n=strlen(s+1),stk[++top]=s[1],pos[1]=1,f[1][0]=s[1]; 14 for(int i=2;i<=n;i++) 15 { 16 while(top-1>=0 && stk[top]>s[i] && cnt+1<=m)top--,cnt++; 17 stk[++top]=s[i],pos[top]=i; 18 f[i][0]=s[i]; 19 } 20 for(int j=1;j<=log2(n);j++) 21 for(int i=1;(i+(1<<j)-1)<=n;i++) 22 f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]); 23 for(int i=1;i<=t;i++) 24 { 25 scanf("%d%d",&a,&k); 26 int l=max(1,pos[a]-k),r=min(n,pos[a]+k),lg=log2(r-l+1); 27 printf("%c\n",max(f[l][lg],f[r-(1<<lg)+1][lg])); 28 } 29 return 0; 30 }