1.描述
ST表是可重复贡献问题,采用了倍增和动态规划的思想
可重复贡献问题:没一个元素会重复贡献,添加一些元素不会影响最终结果
可重复贡献问题应用:1.求最大公约数(gcd)
2.求区间最大值和最小值
可采取打表思想:预处理时间复杂度O(n log n) 查询时间复杂度O(1)
2.操作
1.预处理
f[i][j]表示以i为起点长度为个元素的最大值
初始化:f[i][0]=a[i]
动态转移方程: f[i][j]=max(f[i][j-1],f[i+2^(j-1)][j-1]);
本意来说就是求区间为从i开始,长度为的区间最大值拆分成区间从i开始,长度为2^(j-1)的区间最大值和区间从i+2^(j-1)开始,长度为2^(j-1)的区间最大值的最大值
代码如下:
for(int j=1;j<=log2(n);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]);
}
}
2.查询
代码如下:
int k=log2(r-l+1);
int ans=max(f[l][k],f[r-(1<<k)+1][k]);
cout<<ans<<endl;
求区间[l,r]的最大值可以分成去区间[l,l+2^k-1]的最大值和区间[r-2^k+1,r]的最大值的最大值
也就是求max(f[l][k],f[r-(1<<k)+1][k])
3.换底公式
log a ( b ) / log a ( c ) = log c ( b )
3.例题
A Magic Lamp
题目描述
kiki喜欢旅行。 有一天她发现了一盏神灯,可惜神灯里的精灵并不那么善良。 kiki必须回答一个问题,然后精灵才能实现她的一个梦想。
问题是:给你一个整数,你可以精确地删除 m 位数字。 剩余的数字将形成一个新的整数。 你应该把它减到最小,并且不允许更改数字顺序。 现在你能帮助kiki实现她的梦想吗?
输入描述
有若干组测试用例。
每个测试用例将包含一个给定的整数(最多可以包含 11001100 位数字。)和整数 m(如果整数包含 n 位数字,则 m 不会大于 n)。
给定的整数将不包含前导零。
输出描述
对于每种情况,在一行中输出您可以获得的最小结果。
如果结果包含前导零,则忽略它。
样例输入
178543 4
1000001 1
100001 2
12345 2
54321 2
样例输出
13
1
0
123
321
思路:
本题就是要保留n-m位,
将f数组定义为pair类型,存储数字和序号
保留的第一位数字一定在(1,m+1)
第二位数字一定在(x+1,m+2)
#include <bits/stdc++.h>
using namespace std;
const int maxn=2001;
int a[maxn];
pair<char,int> f[maxn][maxn];
int main(){
string s;
int m;
while(cin>>s>>m){
for(int i=1;i<=s.size();i++){
f[i][0]=make_pair(s[i-1],i);
}
int n=s.size();
for(int j=1;(1<<j)<=n;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]);
}
}
int l=1,r=m+1,tmp=n-m;
bool flag=false;
while(tmp--){
int k=log2(r-l+1);
char ans=min(f[l][k],f[r-(1<<k)+1][k]).first;
int ans2=min(f[l][k],f[r-(1<<k)+1][k]).second;
if(ans!='0'||(ans=='0'&&flag)){
cout<<ans;
flag=true;
}
l=ans2+1;
r++;
}
if(!flag) cout<<0;
cout<<endl;
}
return 0;
}