我们来看这么一个问题:您有 个数和 次操作,每次操作求区间 中的最大值。没有修改且 。
考虑最最简单的算法——暴力,你可以写出如下的代码:
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
for(int i=1;i<=m;i++){
register int l,r;
scanf("%d%d",&l,&r);
register int ans=a[l]-1;
for(int j=l;j<=r;j++)
ans=max(ans,a[j]);
printf("%d\n",ans);
}
然而,它的时间复杂度是
,一定会超时 (除非数据巨水) 。
我们考虑用倍增算法优化它,记 表示 中的最大值,即从 起连续的 个数中的最大值。
首先考虑如何把所有的 算出来,事实上,我们可以把它分成两部分,前半部分为 ,后半部分为 ,所以我们有如下的递推式:
这样做的时间复杂度为 。
for(int j=1;j<=LogN;j++)
for(int i=1;i+(1<<j)-1<=n;i++)
f[i][j]=max(f[i][j-1],f[i+(1<<j-1)][j-1]);
然后考虑如何求最大值。因为取最大值时区间可以重叠,所以我们可以把区间分成重叠的两部分(记 ): 。所以最大值为:
return max(f[l][s],f[r-(1<<s)+1][s]);
【小技巧】: 因为现成的log2
函数很慢,所以我们用
的时间复杂度递推出所有数字的log
值。
总的时间复杂度:
洛谷P2412
【题意】: 蒟蒻HansBug
在一本英语书里面找到了一个单词表,包含
个单词(每个单词内包含大小写字母)。现在他想要找出某一段连续的单词内字典序最大的单词。该题目单词字典序比对过程中大小写不敏感,但是输出必须输出原单词。
【思路】: ST算法
的模板题,但是不是普通的整数ST算法
,是处理字符串的算法。
【代码】:
#define gc getchar()
#define g(c) isdigit(c)
inline int read(){
char c=0;int x=0;bool f=0;
while (!g(c)) f=c=='-',c=gc;
while (g(c)) x=x*10+c-48,c=gc;
return f?-x:x;
}
string MIN(string a,string b){
string x=a,y=b;
for(int i=0;i<x.size();i++)
x[i]=tolower(x[i]);
for(int i=0;i<y.size();i++)
y[i]=tolower(y[i]);
return x>y?a:b;
}
const int N=50100,logN=17;
string f[N][logN],a[N];
int n,m,Log[N];
string answer(int x,int y){
register int s=Log[y-x+1];
return MIN(f[x][s],f[y-(1<<s)+1][s]);
}
int main(){
freopen("t1.in","r",stdin);
n=read();m=read();Log[0]=-1;
for(int i=1;i<=n;i++){
cin>>a[i];f[i][0]=a[i];
}
for(int i=1;i<=n;i++)
Log[i]=Log[i>>1]+1;
for(int j=1;j<=logN;j++)
for(int i=1;i+(1<<j)-1<=n;i++)
f[i][j]=MIN(f[i][j-1],f[i+(1<<j-1)][j-1]);
for(int i=1;i<=m;i++){
register int x=read(),y=read();
printf("%s\n",answer(x,y).c_str());
}
return 0;
}